Extensions and unknown fields

Copy Markdown View Source

PB preserves data the current schema does not have a declared field for, rather than dropping or rejecting it. Both live under reserved dunder keys so they cannot collide with real .proto field names.

Unknown wire fields

Wire fields the schema does not recognise are preserved under :__unknown_fields__ as a list of PB.UnknownField structs, in observed order:

%{
  name: "Alice",
  __unknown_fields__: [%PB.UnknownField{...}]
}

They round-trip: supply them back on encode and PB emits the preserved raw wire bytes. Unknown fields are not schema-modeled, so most applications can ignore them. Struct representations drop them by default — see the preserved_unknown_fields: policy in Decoding into structs.

Extensions

Known extension values are resolved through the schema and stored under :__extensions__, keyed by fully-qualified extension name:

%{
  __extensions__: %{"my.pkg.priority" => 5}
}

Once resolved, extensions behave like declared fields. Enumerate the extensions a schema knows with PB.Schema.list_extensions/1 and look them up with PB.Schema.fetch_extension/2 / PB.Schema.extension!/2 (see Introspection).

Because extensions are schema-modeled data, a struct representation that has nowhere to put them rejects them at compile time by default. Opt into an explicit storage field when you need to carry them — see the extensions: policy in Decoding into structs.