What does gRPC-Web's text variant change about a response body, and what trips decoders up?
answer
- same protocol, different alphabet
- the whole body is encoded
- roughly a third more bytes
- padding shows up mid-body
- chunk boundaries ignore frame boundaries
basics
~20 sThe text variant, application/grpc-web-text, base64-encodes the whole body - frames, payloads and trailer block alike. A client asks for it with Accept: application/grpc-web-text. Its trap is that base64 padding does not line up with frame boundaries.
solid answer
~40 sgRPC-Web defines a text form, `application/grpc-web-text`, in which the body is base64-encoded rather than raw bytes; a client asks for it with `Accept: application/grpc-web-text`. It exists for paths and client environments that cannot carry a binary body reliably, and it costs roughly a third more bytes. The decoding trap is that a streaming response in this form is a sequence of independently encoded base64 chunks: `=` padding can appear part-way through the body, where it means nothing more than the end of one chunk. A decoder that assumes one base64 document, or that treats the first padding it sees as end of stream, will stop early or corrupt the frames that follow - and the padding does not line up with the boundaries of the message frames underneath it either.
code
http · 7 linesPOST /vet.dispensary.v1.StockService/GetItem HTTP/1.1
host: dispensary.example
content-type: application/grpc-web-text
accept: application/grpc-web-text
x-user-agent: grpc-web-javascript/0.1
AAAAAAkKB2JhcmNvZGU=go deeper
Know that the variant has a base64 form named application/grpc-web-text, used where a binary body cannot be carried safely, and that it costs extra bytes. The decoding details can wait.
Explain that the whole body is encoded, that the client asks with the matching Accept field, and that padding may appear part-way through because the body is a run of independently encoded chunks.
Show the two-layer receiver: decode chunks as they arrive, then run the frame reader over the decoded bytes. Name the production failure - a decoder that works on short responses and truncates long streaming ones at the first padding.
The trade-off worth stating is when the roughly one-third byte cost buys enough compatibility to be worth it, versus fixing the path or the client runtime that cannot carry a binary body in the first place.
## What the text variant is gRPC-Web normally carries raw bytes: each frame is a five-byte prefix and a payload, written straight into the body. The **text variant** keeps exactly that framing and then base64-encodes the result. Its media type is `application/grpc-web-text`, and a client asks a server for it by sending `Accept: application/grpc-web-text` on the request. What is encoded is **everything in the body** - the flag bytes, the four-byte lengths, the message payloads and the final trailer block. It is not a different protocol; it is the same bytes in a different alphabet. ## Why it exists, and what it costs It exists for environments and paths that cannot be trusted with a binary response body: a client runtime that only hands back decoded text, or an intermediary that mangles bytes it believes to be text. Base64 uses four output characters for every three input bytes, so the body grows by roughly a third - a real cost on a streaming response, and the reason the binary form is the default whenever it works. ## The trap: padding in the middle Base64 pads the final group of an encoding with `=` so the output length is a multiple of four. In a single self-contained document, padding therefore appears only at the very end - and that is the intuition most decoders are built on. A streaming gRPC-Web text response breaks it. The body is produced as a **sequence of independently encoded chunks**, so each chunk carries its own padding, and `=` can turn up part-way through the body. Two consequences follow: - **Padding is not an end-of-stream signal.** A decoder that stops at the first `=` truncates the response and, quite likely, loses the trailer frame - which turns a successful call into a call with no status. - **Padding does not line up with the frames underneath.** A chunk boundary falls wherever the sender happened to flush; the message frames are a separate layer with their own boundaries, and there is no relationship between the two. A decoder cannot use padding to find a frame, and a frame reader cannot assume a frame begins where a chunk does. The correct shape for a receiver is two independent layers: 1. Decode base64 **chunk by chunk as it arrives**, tolerating padding wherever it appears, and emit the decoded bytes into a buffer. 2. Run the ordinary frame reader over that buffer: five-byte prefix, then payload, then the next frame - and the frame with the top bit of its first byte set is the trailing status. Getting this wrong produces a memorable class of bug. The call works in development, where responses are short enough to come out as one chunk with padding only at the end, and fails in production on exactly the long streaming responses the feature exists for. ## Recognising it on the wire A text-variant body looks like an opaque run of base64 characters, so the media type is the only clue in the headers. Decoding by hand is worth doing once: a small frame such as a nine-byte message preceded by its five-byte prefix comes out as twenty base64 characters ending in a single `=`, and seeing the `00 00 00 00 09` prefix reappear after decoding is what makes the layering concrete. ## Where this sits in an interview This is a differentiator, not a screening question. Most engineers who have used the variant reach for the binary form and never think about the text one. It comes up when a candidate says they have debugged a gRPC-Web path in production, and the follow-up is what the `-text` media type was for and why their decoder buffered the whole body. A candidate who names the media type, the `Accept` field the client sends, the roughly one-third size cost and the mid-stream padding has clearly worked with it rather than read about it.
- Which side asks for the text variant, and how?The client asks, by sending `Accept: application/grpc-web-text` on the request; the server answers with `content-type: application/grpc-web-text` when it honours the request. The request body is encoded the same way. It is a negotiation the client starts, not something a server imposes.
- Why does a decoder that buffers the whole body then decodes once still misbehave on a stream?Two reasons. It defeats incremental delivery, because nothing is decoded until the response ends - which for a long server-streaming call may be never. And a single decode over concatenated chunks hits the interior padding as though it were a malformed document, so it either errors or silently drops bytes at each chunk boundary.
saying these in an interview costs you the question
- Thinks only the trailer block is base64-encoded
- Treats the first `=` padding as the end of the stream
- Assumes padding marks a message-frame boundary
- Says the text variant is a different protocol, not an encoding
- Believes base64 encoding is free in bytes on the wire