Besides the numeric grpc-status, what may a failed gRPC call return to explain itself, and under what constraints?
answer
- one number, one sentence, one payload
- the number is the only contract
- narrow escaping, not URL encoding
- details only when not OK (0)
- the payload's code must match grpc-status
basics
~20 sTwo optional companions, both in the call's trailing metadata: grpc-message, a percent-encoded human-readable string, and grpc-status-details-bin, a base64 payload holding a google.rpc.Status whose code must agree with grpc-status and which is only sent when the status is not OK.
solid answer
~40 sA failure comes back as up to three things. **`grpc-status`** is the number, and the only part callers should branch on. **`grpc-message`** is an optional human-readable string, percent-encoded with gRPC's own narrow rule — only the percent byte itself and bytes outside printable ASCII are escaped, so an ordinary space travels literally. **`grpc-status-details-bin`** is optional and carries a base64-encoded, serialized `google.rpc.Status` for machine-readable detail. Its constraints matter: it is only sent when the status is **not** `OK (0)`, its own `code` field must agree with `grpc-status`, and its message should agree with `grpc-message`. All three ride in the same trailing metadata block, so their combined size is bounded — a stack trace in `grpc-message` is a real operational mistake.
code
http · 3 linesgrpc-status: 9
grpc-message: no prior authorisation on file
grpc-status-details-bin: CAkSHm5vIHByaW9yIGF1dGhvcmlzYXRpb24gb24gZmlsZQ==go deeper
Know that a failed gRPC call returns a number and may also return a short human-readable message, and that your code should read the number rather than the sentence.
Explain the three fields and their roles, including that grpc-message uses a narrow percent-encoding where an ordinary space is not escaped, and that the details payload is a base64 -bin value.
Demonstrate the constraints and why they exist: details only on a non-OK status, the payload's code agreeing with grpc-status, and the size discipline that keeps diagnostics out of a bounded metadata block.
Decide what structured failure detail your services promise callers at all. Every field a caller starts branching on becomes part of the contract, and removing one later costs as much as removing a method.
## Three things, with three different jobs When a gRPC call fails, up to three fields come back in the call's trailing metadata, and confusing their roles is how a service ends up with callers parsing English. | field | required | for | audience | |---|---|---|---| | `grpc-status` | yes | the numeric code from the status enum | code | | `grpc-message` | no | a short human-readable description | people | | `grpc-status-details-bin` | no | structured, machine-readable detail | code | The direction is fixed: the **server** produces all three; the **client** reads them. There is no client-side equivalent. ## grpc-message and its unusual escaping `grpc-message` is a string, and metadata values are limited to printable ASCII — so a message that contains anything else needs escaping. gRPC uses a **percent-encoding** in the style of RFC 3986 section 2.1, but with a deliberately narrow escape set: - bytes in the printable ASCII range travel **literally**, with one exception; - the **percent byte itself** is always escaped, because it introduces an escape; - any byte **outside** printable ASCII is escaped as `%` followed by two hex digits — so a multi-byte UTF-8 character becomes a run of `%XX` sequences. The consequence people get wrong: **an ordinary space is not escaped**. `no prior authorisation on file` travels exactly like that, spaces and all. Reaching for URL-query intuitions here produces `%20` where none belongs and, worse, teaches the idea that this is generic URL encoding. Three operational rules follow from the field's nature: 1. **Never branch on it.** It is free text, it is not part of the contract, and it changes when someone rewrites a log line. 2. **Never localise the decision on it.** It is one string, produced by the server, in whatever language the server writes. 3. **Keep it short.** It shares a size-bounded metadata block with everything else the call returns. ## The details payload `grpc-status-details-bin` is a `-bin` metadata key like any other: its value is **base64** on the wire and decodes to bytes. Those bytes are a serialized **`google.rpc.Status`** message, which carries a code, a message and a list of detail messages whose own schemas are defined elsewhere in protobuf terms. The binding rules are what an interviewer is after: - It is sent **only when the status is not `OK (0)`**. A successful call has nothing to detail. - Its `code` field **must agree** with the `grpc-status` value. A response saying `grpc-status: 5` while the payload's code says `9` is self-contradictory, and a caller has no principled way to pick. - Its message should agree with `grpc-message` rather than telling a different story. - It is **optional**, and not every stack surfaces it, so a client must behave correctly when only `grpc-status` arrives. Because the payload is base64, it is about **four bytes on the wire for every three** of serialized data — a third larger — and it competes for the same bounded metadata budget as everything else in the block. ## Why the agreement rule exists At the pharmacy counter, the counter software branches on the numeric status: a code that means 'the plan needs a prior authorisation' puts a different instruction on the screen from a code that means 'we could not identify you'. If the trailer's number and the payload's number disagree, two layers of the same caller can take two different branches from one response — the transport-level code path sees one failure and the application-level code path sees another. That is why the specification requires agreement instead of leaving precedence to the client. ## What to put where - The **status code** carries the classification. It is the contract. - The **details payload** carries anything a caller must act on programmatically — an identifier, a structured reason, a field reference. - The **message** carries a sentence for a person reading a log, and nothing else. And two things belong in none of them: bulk data, which belongs in a response message where the schema describes it, and internal diagnostics such as stack traces or query text, which leak implementation detail to every caller and consume a metadata budget that is not there to hold them.
- Why should a caller never branch on the text of a gRPC grpc-message field?Because it is free text with no contract behind it: it is written for a person, changes whenever someone rewrites the sentence, and is in whatever language the server produced. Branching belongs on `grpc-status`, or on the structured detail payload if one is sent.
- A gRPC response carries grpc-status: 0 and a grpc-status-details-bin value. What is wrong?The details payload is only sent when the status is not `OK (0)`. A successful call has no failure to detail, so the pair is contradictory and a caller reading it has no defensible interpretation.
- Is a space escaped in a gRPC grpc-message value?No. The escape set covers the percent byte and any byte outside printable ASCII; a space is printable ASCII and travels literally. Writing `%20` imports URL-query habits that this encoding does not share.
saying these in an interview costs you the question
- Branches on grpc-message text instead of the status code
- Thinks grpc-message is encoded like a URL query string
- Sends a details payload alongside an OK (0) status
- Lets the details payload carry a different code than grpc-status
- Assumes a details payload is always present on a failure
- Packs stack traces into trailing metadata on every error