Skip to content

Every field on the wire is wrapped in an "envelope." The envelope tells the decoder which field number it is and how to read the payload. Both get packed into a single tag value, encoded as a varint.

Wire typeProtobuf types
0 (Varint)
int32int64uint32uint64boolenum
1 (I64)
fixed64sfixed64double
2 (LEN)
stringbytesmessagealso used for packed repeated scalars
3 (SGROUP)group start (deprecated)
4 (EGROUP)group end (deprecated)
5 (I32)
fixed32sfixed32float

Varint Fields (Wire Type 0)

Most numeric types use Wire Type 0. The length is implicit because the decoder reads bytes one by one until it finds a byte where the MSB is 0. That's the same continuation-bit rule from the varint section earlier, doing the delimiting on its own.

messageUser {int32age =1;}Schemaage:150Data+Encoded Payload08Tag96 01Value (Varint 150)00001000Field 1Type 010010110 00000001MSB=1 (Cont.)MSB=0 (End)

Fixed-Size Fields (Wire Type 1, 5)

Some types have a known size, so nothing needs to be measured. Wire Type 1 is always 8 bytes, used by double and fixed64. Wire Type 5 is always 4 bytes, used by float and fixed32. The decoder reads that many bytes after the tag.

messageUser {floatheight =2;doubleweight =3;}Schemaheight:3.14weight:80.0Data+Encoded Payload15Tagdb 0f 49 40Value (3.14 float, Little-Endian)00010101Field 2Type 511011011 0000111101001001 01000000Fixed 32-bit (4 Bytes)19Tag00 00 00 00 00 00 54 40Value (80.0 double, Little-Endian)00011001Field 3Type 100000000 00000000 00000000 0000000000000000 00000000 01010100 01000000Fixed 64-bit (8 Bytes)

Length-Delimited (Wire Type 2)

string, bytes, and nested message fields use Length-Delimited encoding. These fields carry an explicit length prefix, encoded as a varint, right after the tag. That prefix tells the decoder exactly how many of the bytes that follow belong to this field.

messageUser {stringname =3;}Schemaname:"Alice"Data+Encoded Payload1aTag05Len41 6c 69 63 65"Alice"00011010Field 3Type 200000101Length 501000001 0110110001101001 01100011 01100101"Alice" ASCII

Packed Repeated Fields

Repeated fields of primitive types use a specialized encoding that avoids repeating the field tag for every element. Edition 2024 packs them by default. Set features.repeated_field_encoding to opt a file, message, or field back out.

EXPANDED
Schema
repeated int32 ids = 1 [features.repeated_field_encoding = EXPANDED];
Values
[1, 2, 3]
Wire Layout
08
Tag
01
Val
08
Tag
02
Val
08
Tag
03
Val

Each element repeats the field tag. High overhead for many small elements.

PACKED (EDITION 2024 DEFAULT)
Schema
repeated int32 ids = 1;
Values
[1, 2, 3]
Wire Layout
0a
Tag
03
Len
01
Data
02
Data
03
Data

Elements are concatenated into a single length-delimited record. One tag for the whole set.

Further Reading

Next

Map Encoding

How Protobuf maps look on the wire: each entry as a repeated key/value message, and why that representation keeps old decoders compatible.