Protobuf Decoder
Decode a raw protobuf message from hex, base64 or the binary file, with no .proto — field numbers, wire types, strings and nested sub-messages, recursively.
This reads the raw wire format, so no .proto is needed — but field names, enum names and declared types are not on the wire, so they cannot be shown. Fields are keyed by number, and a length-delimited value is a guess: text, else a sub-message, else bytes. Show other readings lists what else each value could be.
Decode protobuf without the .proto
Paste a protobuf-encoded message as hex or base64, or open the binary message itself (a .pb or .bin file) with Open file…, and this tool walks the binary wire format byte by byte, with no schema required. It recovers the number, wire type (varint, 64-bit, length-delimited, 32-bit) and value of every field, and recurses into nested sub-messages so you can see the whole tree. Everything runs locally in your browser and the payload is never uploaded.
How the wire format is read
The protobuf binary format records a field number and wire type per field but not the declared type, so length-delimited bytes are interpreted heuristically: printable UTF-8 text is shown as a string, otherwise bytes that parse cleanly as a sub-message are shown as a nested message, and anything else as raw hex. Integers too large for JavaScript are shown as exact decimal strings. Because the declared type is missing, Show other readings lists what else each value could be — a varint as a signed or zigzag integer or a bool, fixed-width bytes as a float or double, a length-delimited field as a packed list of numbers — and a raw-bytes field that is exactly a list of varints is shown as one without asking.
Input and output
Hex is the default, and it reads the bytes however they are laid out — run together, spaced, one per line, joined with colons, commas or dashes, or with a 0x before each byte as a C array or a debugger dump writes them (0x08, 0x96, 0x01). Auto-detect works out whether the text is hex or base64 — URL-safe and unpadded base64 included — and says which it read. The Tree and JSON switch shows the decoded message as the tree or as the JSON that Copy JSON and Download take, keyed by field number, and a line above the result gives the bytes read, the fields found (nested ones counted too) and how long the decode took.
Frequently asked questions
Do I need the .proto file to decode a message?
No. The protobuf wire format is self-describing enough to recover field numbers, wire types and raw values without any schema. Declared field names and exact types (for example int32 versus sint32) are not stored on the wire, so they cannot be shown, but the structure and values can — and Show other readings puts each value’s other possible readings — signed, zigzag (sint32), float and the rest — under it, so you can pick the one that makes sense.
Why does my gRPC payload fail with a field number 0 error?
gRPC frames each message with a 5-byte prefix (one compression-flag byte plus a 4-byte big-endian length) before the protobuf bytes. Remove those 5 bytes and decode the remainder.
How are strings, bytes and nested messages told apart?
For a length-delimited field the decoder shows printable UTF-8 as a string; if the bytes are not printable text but parse cleanly as a protobuf sub-message they are shown as a nested message; otherwise they are shown as raw hex. This is a heuristic without the schema, so an unusual value may be classified differently than intended. When text was chosen but the same bytes also decode as a sub-message, Show other readings lists that sub-message too.
What if the number shown is not the value I expected?
Tick Show other readings — signed, zigzag, float, packed lists. A varint is then also read as a signed integer (two’s complement), a zigzag sint32/sint64 and a bool; four fixed bytes as sfixed32 and float, eight as sfixed64 and double; and a length-delimited field as a packed list of varints, zigzag values, floats or doubles where its length fits. Readings that would only repeat the plain value are left out, so a varint of 1 that was really a sint32 shows −1 beneath it. A raw-bytes field that is exactly a list of varints shows packed varint: 1, 2, 3 without being asked.
Does it tell hex from base64 by itself, show the JSON, or say how much it decoded?
Yes. Auto-detect, beside Hex and Base64, reads the text as hex first and as base64 — standard or URL-safe, padded or not — when the hex does not parse or does not decode as a message, and says which it read; Hex stays the default. The Tree and JSON switch shows the decoded message as the tree or as the JSON that Copy JSON and Download take, keyed by field number. Beside it one line gives the bytes read, the fields found, the count with nested ones included, and how long the decode took, said as under a millisecond rather than a fraction the timer cannot measure.
Can I paste hex with 0x in front of every byte?
Yes. The Hex reading takes a 0x at the start of the text or before each byte group, so a C array or a debugger dump — 0x08 0x96 0x01, the same with commas after each byte, or one byte per line — decodes as pasted, and Auto-detect, which tries hex first, takes it too. A 0x in the middle of a group is not a prefix: 10x5 is refused as not hex rather than quietly read as 15.