v10.2 Fix layout switching, pane, and high-DPI display issues See what's new

How to Debug gRPC Calls with HTTP Debugger

Inspect gRPC calls over HTTP/2 — streams, framed messages, metadata, and status

  1. Call HTTP/2 stream · :path
  2. Metadata request / response headers
  3. Messages length-prefixed Protobuf
  4. Status trailers · grpc-status

gRPC is a high-performance RPC framework that serializes messages with Protocol Buffers and carries them over HTTP/2. It was developed by Google and open-sourced in 2015; the official site is grpc.io. Unlike WebSocket, which provides a raw channel for streaming unstructured data (usually JSON) over a persistent connection, gRPC focuses on strictly typed remote procedure calls. AI agent backends often use it for typed calls to model, tool, vector search, and inference services.

To debug gRPC calls you cannot just read a URL and a JSON body the way you do with REST — the method lives in an HTTP/2 path, the payload is binary Protobuf wrapped in a length-prefixed frame, and the real outcome is reported in trailers after the response, not in the HTTP status line. The sections below cover what a gRPC call actually looks like on the wire, the practical ways to observe it, and how to inspect live gRPC traffic without standing up a proxy.

What it takes to debug gRPC calls

A gRPC call is a single HTTP/2 stream. The request opens the stream with a HEADERS frame, sends one or more messages in DATA frames, and the server replies with its own HEADERS, DATA, and a final trailing HEADERS frame. Because HTTP/2 multiplexes many streams over one TCP connection, frames from different calls are interleaved on the wire and only the stream ID ties them back together.

The request line you know from HTTP/1.1 is replaced by HTTP/2 pseudo-headers. The RPC method is the :path, formatted as /package.Service/Method, and the content type is application/grpc. Here is the logical, decoded view of a unary call — the bytes are HPACK-compressed and TLS-encrypted on the actual wire:

# HEADERS frame — request opens the stream
:method: POST
:scheme: https
:path: /routeguide.RouteGuide/GetFeature
:authority: api.example.com:443
content-type: application/grpc
grpc-accept-encoding: gzip, identity
te: trailers

# DATA frame — one length-prefixed gRPC message
# byte 0      : compression flag (0 = none, 1 = compressed)
# bytes 1..4  : message length, big-endian uint32
00 00 00 00 12  <18 bytes of Protobuf>

# HEADERS frame — response
:status: 200
content-type: application/grpc

# HEADERS frame with END_STREAM — gRPC trailers
grpc-status: 0
grpc-message:

The length-prefixed message framing

Every gRPC message inside a DATA frame is wrapped in a 5-byte prefix: one byte for the compression flag (0 for none, 1 for compressed) followed by a big-endian 32-bit length. A single DATA frame can contain several concatenated length-prefixed messages, and one logical message can also span multiple frames. To read a payload you decode the prefix, slice out exactly that many bytes, and — if the compression flag is set — inflate it using the algorithm named in grpc-encoding (typically gzip or deflate). Only then do you have the raw Protobuf bytes.

Status lives in the trailers, not the HTTP status

This is the single most common source of confusion. The HTTP/2 response almost always carries :status: 200, even when the RPC failed. The real result is the grpc-status code, usually in the trailing HEADERS frame, with an optional human-readable grpc-message. Some failures return a trailers-only response, where grpc-status appears in the only response HEADERS block with END_STREAM. A call that returns HTTP 200 but grpc-status: 5 is a NOT_FOUND error, not a success. The numeric codes are fixed:

CodeNameTypical cause
0OKSuccess
3INVALID_ARGUMENTBad request payload or field
4DEADLINE_EXCEEDEDCall exceeded its timeout
5NOT_FOUNDRequested entity does not exist
7PERMISSION_DENIEDCaller lacks permission
12UNIMPLEMENTEDMethod/service not registered (often a routing or version mismatch)
14UNAVAILABLEServer unreachable, connection dropped, or overloaded
16UNAUTHENTICATEDMissing or invalid credentials metadata

Why gRPC calls are hard to debug

  • The payload is binary. Protobuf has no field names on the wire — just tag numbers and bytes — so you cannot eyeball it like JSON without the .proto schema.
  • It is encrypted. Production gRPC runs over TLS, so a plain packet capture shows only ciphertext unless you can decrypt it.
  • Streams are multiplexed. One connection carries many interleaved calls; reconstructing a single request/response pair means following its stream ID.
  • Status hides in trailers. Tools that only show HTTP status will report success on a failed call.
  • Client tools only see their own calls. A CLI client tells you what it sends, not what your already-running service or app is actually putting on the wire.

Ways to debug gRPC calls

Each approach answers a different question. Active clients let you make a call; logging and tracing tell you what the process did; on-the-wire capture shows what actually crossed the network.

Approach What it shows Needs .proto? Sees existing app traffic?
Server logging / interceptors Decoded messages and status from inside your code No (you have the types) Only services you own and can redeploy
Manual test clients Calls you craft, with field-level Protobuf decoding when reflection or a .proto schema is available Yes, unless server reflection is on No — only the calls the client itself sends
Env tracing (GRPC_TRACE, Channelz) Internal channel/connection state and verbose library logs No Only processes you can configure
Packet capture Raw HTTP/2 frames and gRPC messages Yes, plus a TLS keylog file to decrypt Yes, but with heavy setup
Proxy-less on-the-wire capture Live decrypted calls reassembled per stream, with status and metadata decoded No (raw payload bytes today) Yes — any process on the machine

For services you own, structured logging interceptors with correlation IDs are the cheapest win. Manual test clients are useful when you want to craft a controlled call against a known endpoint, especially when server reflection or local .proto files are available.

What these approaches do not answer well is the question "what is my running browser, desktop app, or microservice actually sending right now?" That requires capturing the real traffic, already decrypted, off the wire.

Debug gRPC calls with HTTP Debugger

HTTP Debugger captures gRPC traffic and keeps it visible in the same grid as the rest of the session. Each gRPC request shows its method, HTTP version, URL, status, content type, and captured message count, then opens into a dedicated Messages pane for per-message inspection.

  1. Reproduce the gRPC call in the client application. HTTP Debugger shows the captured request in the grid with its HTTP status, gRPC status, and content type, such as 200 / gRPC OK and application/grpc, plus the number of messages captured for that request. If the grid contains heavy mixed traffic, click on All Types in the toolbar and select gRPC to see only gRPC rows.
    HTTP Debugger main grid with a gRPC request selected, status 200 / gRPC OK, type application/grpc, and a message-count badge HTTP Debugger type filter dropdown with DATA, GENERAL, and STREAMING groups, including a gRPC entry
  2. Click the message-count badge in the gRPC row. HTTP Debugger opens the gRPC Messages pane on the right, with the captured messages for the selected request. You can filter the messages by type, direction, or payload text. Selecting a message shows its details: the Info tab displays protocol, RPC, and message metadata, while Text, Raw, and Hex tabs show the payload in the available formats.
    gRPC Messages pane Info tab showing hello.HelloService/BidiHello over HTTP/2, connection ID 34736, stream ID 1, and per-message request details gRPC Messages pane Text tab showing a decoded streaming message payload
  3. The Info tab groups the key gRPC call parameters into Call, Request, Response, and Trailers.

    Call

    • Protocol — the HTTP version, with the negotiated ALPN token added when it differs (for example h2).
    • Connection ID and Stream ID — the HTTP/2 connection and the individual stream that carried the call.
    • RPC — the parsed service/method name from the :path.

    Request and Response

    • Request Messages / Response Messages — the number of logical gRPC messages in each direction: one each for a unary call, many for a streaming call.
    • Request Captured Payloads / Response Captured Payloads — the number of message payloads actually captured. Select one in the pane to inspect it.
    • Encoding and Accept-Encoding — the per-message compression, listed alongside the gRPC metadata headers and the request's :method, :authority, and :path pseudo-headers.
    • gRPC Status and gRPC Message — the real call result, taken from the response trailers. The HTTP status is almost always 200 whether or not the call succeeded.

    Trailers

    • Declared Trailers — the trailer names the response promised in its headers, followed by the trailer values that actually arrived after the body.

Common gRPC debugging scenarios

"The HTTP response is 200 but the client reports an error"

The transport succeeded; the RPC did not. Read the grpc-status from the trailers. A 12 UNIMPLEMENTED usually means the method name or package in :path does not match what the server registered — often a stale stub or a versioning mismatch. A 16 UNAUTHENTICATED points at missing or malformed credentials in the request metadata.

"Calls intermittently fail with DEADLINE_EXCEEDED"

Use the per-message time offsets to see how long the server took before the deadline fired. If the request messages were sent quickly but the first response message arrives late, the bottleneck is server-side processing, not the client deadline being too aggressive.

"A message body is empty or garbled"

Check the compression flag and grpc-encoding. If a peer sends compressed messages with an encoding the other side did not advertise in grpc-accept-encoding, decoding fails. Seeing the raw framing next to the metadata makes the mismatch immediate.

"Auth works in one client but not another"

Compare the request metadata between the two calls. gRPC credentials travel as metadata headers (for example authorization); capturing both calls shows exactly which header is present, missing, or differently formatted.

Limitations

On-the-wire capture shows you exactly what crossed the network, but it does not own your schema. Today HTTP Debugger displays binary Protobuf as raw bytes (hex) rather than decoding it into named fields; text and JSON-codec payloads render directly. Field-level Protobuf decoding still requires a schema source such as a .proto file or server reflection. gRPC traffic in HTTP Debugger is read-only — request modification and auto-reply rules apply to regular HTTP/HTTPS traffic, not gRPC streams. For everything around the call — headers, timing, sizes, and the raw HTTP/2 protocol exchange — the HTTP analyzer and per-request streaming inspection cover the rest of the picture. The same workflow applies to other real-time protocols — see the guides on debugging WebSocket connections and SSE streams.

FAQ

Can I see gRPC calls in browser dev tools?

Only partially, and mainly for browser-originated gRPC-Web traffic. Browser dev tools do not decode a native gRPC client's traffic — the full HTTP/2 stream, binary Protobuf framing, and trailers. For native gRPC, use a tool that captures the client process, reassembles the stream, and decodes the framing.

How do I decode a Protobuf message without the .proto file?

You cannot fully reconstruct field names without the schema, because Protobuf puts only tag numbers and wire types on the wire. You can still read raw bytes and partially infer structure from the tags. For named-field decoding you need the .proto file or server reflection.

Can packet captures decode gRPC traffic?

Yes, with setup. A packet capture can show low-level HTTP/2 frames and gRPC framing, but TLS-encrypted traffic must be decrypted first and field-level Protobuf decoding still needs the matching .proto schema.

Why does my gRPC call return HTTP 200 but still fail?

Because gRPC reports its result in grpc-status, not the HTTP status. The HTTP/2 response is normally 200 regardless of the RPC outcome. A non-zero grpc-status (for example 5 NOT_FOUND or 16 UNAUTHENTICATED) is the actual error, whether it appears in trailers or in a trailers-only response.

HTTP Debugger Pro

Windows 11 / 10

A proxy-less HTTP/HTTPS sniffer for Windows that captures traffic system-wide from any process.

Download free 7-day trial

No registration required · Trusted since 2007