.proto file, without modifying the message definition. They are primarily a proto2 feature, but they are also used in proto3 and editions for custom options.
What are extensions?
An extension is a field that belongs to a message type but is declared in a different scope — often in a separate.proto file. The message type must explicitly reserve a range of field numbers for extensions. Anyone with a valid field number within that range can define an extension field.
Extensions look and behave like regular fields at the wire format level. The encoded bytes are identical to a regular field with the same number and type. The difference is in how the API handles them: extension fields are accessed through dedicated getter and setter methods rather than generated field accessors.
Declaring extension ranges
A message that allows extensions must declare one or more extension ranges using theextensions keyword:
.proto syntax treats both ends as inclusive. Multiple disjoint ranges are allowed:
Defining extensions
Extensions are declared using anextend block that names the message being extended. The extension fields must use field numbers within the message’s declared extension ranges:
extend block can appear at the top level of a .proto file or inside a message definition:
Using extensions in C++
Generated C++ code for extensions does not create regular field accessors. Instead, you useHasExtension, GetExtension, SetExtension, MutableExtension, and ClearExtension:
MutableExtension to get a mutable pointer:
Using extensions in Java
In Java, extension fields are accessed through theExtensionLite and GeneratedExtension types:
Using extensions in Python
Extensions vs. regular fields
Wire format
Wire format
Extensions are encoded identically to regular fields. A parser that does not know about a particular extension will treat it as an unknown field, preserving the data through serialization round-trips.
API access
API access
Regular fields have generated typed getter and setter methods (
set_foo(), foo()). Extensions require explicit calls to GetExtension / SetExtension with the extension descriptor as a parameter. This makes the API slightly more verbose.Ownership
Ownership
Regular fields are owned by the message type and defined in its
.proto file. Extensions are owned by the code that defines the extend block, which can be in an entirely separate package or binary.Discovery
Discovery
You can enumerate all registered extensions of a message type using
DescriptorPool::FindAllExtensions(). Regular fields are enumerated through Descriptor::field_count() and Descriptor::field().Nested extensions
Extensions can be nested inside message types. This is a common pattern for grouping related extensions and avoiding name collisions at the package level:Extensions in proto3 and editions
Insyntax = "proto3", regular messages cannot declare extension ranges or have extensions defined on them. Extensions in proto3 are restricted to extending the *Options messages from descriptor.proto, which is the mechanism that implements custom options.
In editions, this restriction is lifted. Any message in an editions file can declare extension ranges and have extensions defined on it — the same as proto2.
For new designs in editions, consider whether
oneof or a well-known type like google.protobuf.Any better fits your use case. Extensions are primarily useful when you need different teams or packages to independently extend a shared message type.Finding extensions at runtime
You can look up all known extensions of a message type using theDescriptorPool: