Also available in: Español · Português · Français · العربية
Protobuf decoder
Decode Protocol Buffers bytes without the .proto file, and see exactly which parts of the message can be recovered and which cannot.
What a protobuf decoder can and cannot recover
Protocol Buffers encode a message as a sequence of fields, each one a small header followed by a payload. The header is a single varint holding the field number and a three-bit wire type, and every payload is either self-delimiting or preceded by its length. That is enough to walk the whole message without knowing anything about it, which is why decoding protobuf without a .proto file works at all.
It is also all you get. Field names are not on the wire in any form, so a decoder can tell you that field 3 holds the number 1500 but never that the field is called timeout_ms. That is not a limitation of this tool; the names simply are not there, and no amount of cleverness recovers them.
Two things beyond that are genuinely undecidable, and they are the reason this page shows several readings per field. A varint is a valid int32, int64, uint32, uint64, bool and enum all at once — and sint32 and sint64 use zigzag encoding, so the number itself changes: the varint 3 is 3 as a uint and −2 as a sint. Meanwhile a length-delimited field might be a string, raw bytes, a nested message, or a packed repeated numeric field, and nothing in the encoding distinguishes them.
How to use it
- Paste the bytes. Hex or base64, with or without spaces, colons or 0x prefixes. Bytes copied straight out of a network capture or a log line work as they are.
- Read down the field list. Each field shows its number, its wire type, and every interpretation the encoding permits. Nested messages are expanded in place.
- Treat multiple readings as real ambiguity. Where a field lists more than one, all of them are correct and the wire format does not record which was meant. The counter at the top says how many fields are in that position.
The example worth trying first
The bytes 0a 04 08 2a 10 07 are the default in the box above. They decode as field 1 holding a four-byte payload — and that payload is simultaneously a perfectly good nested message with field 1 set to 42 and field 2 set to 7, and a perfectly good opaque four-byte blob. Both readings are valid. Nothing in those six bytes says which one the sender meant.
This is not a contrived case. It is the normal situation for any length-delimited field, and it is why a decoder that prints one answer is showing you its own guess. A short binary string will often parse as a message by accident; a nested message is always also readable as bytes. The schema is the only thing that resolves it, and if you had the schema you would not be here.
There is one useful thing the format does tell you loudly. Wire types 3 and 4 were the old start-group and end-group markers, deprecated many years ago, and 6 and 7 have never been assigned at all. If the bytes contain one, they are either very old or not protobuf, and this page says so rather than producing plausible nonsense.
Honest limits, and how this was checked
This decodes the wire format and nothing above it. It will not tell you the message type, will not resolve field names, and does not attempt to guess a schema from field numbers. It also does not decode the well-known wrapper types or Any, since both need the descriptors to mean anything.
Where a payload could be several things, the nested-message reading is listed first when it parses, because in real traffic that is usually what it is. That ordering is a convenience and explicitly not a verdict — the string and bytes readings are always shown alongside it, and control-character payloads are deliberately not offered as text even when they are valid UTF-8, because a string reading full of unprintable characters is noise dressed as information.
The implementation is pinned to the encoding documentation's own worked examples rather than to expectations invented here: field 1 as a varint 150 is 08 96 01, field 2 as the string testing, the packed example carrying 3, 270 and 86942, and the published zigzag table. The suite runs 577 assertions with 17 negative controls. It passed on its first run, which is exactly when a test suite deserves the least trust, and breaking the engine on purpose then found three genuine gaps: no fixture had asserted a fixed-width value at all, so reading them big-endian went unnoticed; nothing straddled the signed-reinterpretation boundary; and no payload was both valid UTF-8 and made of control characters, which is the only shape that exercises the text check.
Why is it free?
This is byte parsing, and it runs in the tab you already have open. No server sees your bytes, so there is nothing to bill for and no account to make.
Nothing you paste is uploaded, stored or logged. Captured protobuf frames routinely contain identifiers, tokens and personal data, so that is worth stating plainly rather than leaving you to assume it.