Skip to content

chore(rpc): adopt the prpc empty-body encoding for unit responses - #987

Closed
kvinwang wants to merge 2 commits into
masterfrom
followup/prpc-empty-unit-json-body
Closed

chore(rpc): adopt the prpc empty-body encoding for unit responses#987
kvinwang wants to merge 2 commits into
masterfrom
followup/prpc-empty-unit-json-body

Conversation

@kvinwang

@kvinwang kvinwang commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Follow-up to #875, which dropped a transport-layer workaround that byte-matched null in
the Rocket responder. The problem is fixed properly upstream instead:
Phala-Network/prpc#1.

What changes

prpc-build 0.7.0 encodes a google.protobuf.Empty response as an empty JSON body
instead of the literal null. The decision is made in codegen from the response type, so
a method that legitimately returns a JSON null value is unaffected, and message fields of
type Empty still serialize as null inside the object — only the top-level body changes.
The protobuf codec is untouched; it has always encoded a unit as zero bytes.

Both RequestClient impls in the tree decode through prpc::codec::decode_json_from_slice,
which maps an empty body back to the unit type:

  • ra-rpc::RaClient
  • http-client::PrpcClient — missed in the first revision, caught in review. dstack-cli-core's
    stop_vm / remove_vm go through it and would have reported a failure for a request that
    actually succeeded. A compile check cannot catch this: serde_json::from_slice::<()>
    type-checks fine and only fails on the empty input at run time.

An empty body for any other message type stays an error.

Affected wire format

Every unit-returning JSON RPC now responds with an empty body rather than null:

  • vmm: StartVm, StopVm, RemoveVm, ShutdownVm, ResizeVm, SvStop, SvRemove,
    PullRegistryImage, ...
  • gateway: Exit, ReloadCert, and the admin RPCs
  • kms: Onboard.Finish

Client audit:

Client Status
ra-rpc::RaClient fixed here
http-client::PrpcClient fixed here
vmm web UI — vmmRpc (StartVm/StopVm/RemoveVm/ShutdownVm) unaffected: application/octet-stream, protobuf path
vmm web UI — baseRpcCall (SvStop/SvRemove) unaffected: never parses the response body
vmm-cli.py unaffected: only parses a body that looks like JSON, otherwise returns it as a string
JS SDK send-rpc-request.ts latent: bare JSON.parse(data) would throw, but guest-agent exposes no unit-returning method. Should be hardened before one is added

Verification

  • cargo check / cargo clippy for kms, vmm, gateway, guest-agent, ra-rpc, http-client and
    dstack-cli-core: clean.
  • cargo test -p ra-rpc: pass.
  • Inspected the regenerated kms.rs / vmm.rs: dispatch_json_request emits
    Ok(Vec::new()) for unit methods, dispatch_request (protobuf) is byte-for-byte
    unchanged.

Copilot AI review requested due to automatic review settings August 4, 2026 03:09
prpc-build 0.7.0 encodes a `google.protobuf.Empty` response as an empty JSON
body instead of the literal `null`, matching what the protobuf codec has always
done. Decode responses through `prpc::codec::decode_json_from_slice` so an empty
body maps back to the unit type.

Upstream: Phala-Network/prpc#1
@kvinwang
kvinwang force-pushed the followup/prpc-empty-unit-json-body branch from aaf3b64 to 4abe14c Compare August 4, 2026 03:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates dstack’s RPC stack to align JSON unit (google.protobuf.Empty / ()) responses with upstream prpc-build behavior (empty response body instead of literal null), and adjusts the ra-rpc client to decode that format correctly.

Changes:

  • Bump prpc to 0.6.2 and prpc-build to 0.7.0 (workspace deps).
  • Update ra-rpc’s RequestClient implementation to decode JSON responses via prpc::codec::decode_json_from_slice, which maps an empty body back to the unit type.
  • Refresh Cargo.lock to reflect the dependency updates.

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 1 comment.

File Description
dstack/ra-rpc/src/client.rs Switch JSON decoding to prpc::codec::decode_json_from_slice to support empty-body unit responses.
dstack/Cargo.toml Bump workspace prpc / prpc-build versions to pick up the new unit-response encoding.
dstack/Cargo.lock Lockfile update for the prpc / prpc-build version changes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread dstack/Cargo.toml
Comment on lines +264 to +265
prpc = "0.6.2"
prpc-build = "0.7.0"

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, this was a real runtime break and my compile checks could not have caught it — serde_json::from_slice::<()> type-checks fine and only fails on the empty input at run time.

Fixed in 9887987: PrpcClient now decodes through prpc::codec::decode_json_from_slice as well. There are exactly two RequestClient impls in the tree (ra-rpc::RaClient and http-client::PrpcClient); both are updated now, and I left a comment at each call site so a future edit does not quietly revert to serde_json::from_slice.

`PrpcClient` is the second `RequestClient` impl in the tree and still used
`serde_json::from_slice`, so an empty unit body would fail to parse at runtime.
`dstack-cli-core`'s `stop_vm` / `remove_vm` go through it and would have reported
a failure for a request that actually succeeded.

Compile checks cannot catch this: `serde_json::from_slice::<()>` type-checks
fine and only fails on the empty input at runtime.
@kvinwang

kvinwang commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator Author

Closing: the upstream change this depends on has been reverted.

prpc-build 0.7.0 and prpc 0.6.1 / 0.6.2 are yanked, and Phala-Network/prpc main is back at the pre-change commit. The JSON wire format for unit responses stays null.

Rationale: the empty-body encoding fixed nothing that was broken — the EOF bug in #875 was entirely fixed by the shutdown ordering, which is merged. What it bought was codec symmetry with protobuf; what it cost was a breaking wire change across 26 RPCs, a runtime break in PrpcClient that no compile check could catch, and lost boot-progress reporting for old CVM images on upgraded hosts. null is also valid JSON that every client can parse, which an empty body is not.

@kvinwang kvinwang closed this Aug 4, 2026
@kvinwang
kvinwang deleted the followup/prpc-empty-unit-json-body branch August 7, 2026 13:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants