Protocol Buffers Knowledge Patch
Use this skill when changing .proto schemas, upgrading protoc or a language
runtime, regenerating bindings, maintaining reflection code, or updating
Protobuf build rules. Inspect the repository's compiler, runtime, generated-code,
edition, language, and build-tool versions before applying advice.
Working method
- Identify every
protoc, generator/plugin, runtime, and checked-in generated-code
version in the project. Do not infer compatibility from the shared release number
alone because language packages prepend different majors.
- Read the declared syntax or edition and all imported feature/options files.
Edition behavior can change generated APIs independently of runtime version.
- Match guidance to the project's actual release. Preserve older-version behavior
when the project has not crossed the corresponding change.
- Regenerate bindings after compiler or edition changes. Treat generated files as
outputs, then compile and test each consuming language.
- Exercise malformed input, recursion limits, presence/reflection behavior, arena
ownership, and JSON/text round trips when those surfaces are involved.
Reference index
| Reference |
Topics |
| Compatibility, Builds, and Releases |
Gencode/runtime rules, release numbering and support, CMake, Bazel, protoc, toolchains |
| C++ Runtime and Generated APIs |
C++17, arenas, repeated fields, descriptors, debug output, bounds checks, JSON, flags |
| Editions, Schema, and Descriptors |
Edition 2024/2026 features, visibility, imports, naming, descriptors, custom options |
| Java, C#, and Objective-C |
Java generation and parsing, C# packages and recursion, Objective-C runtime migration |
| PHP, Ruby, and Rust |
Baselines, strict parsing, reflection, generated typing, RBS, Rust views and traits |
| Python Runtime and Generated APIs |
Python baselines, removed APIs, maps, descriptors, formatting, upb, NumPy and text parsing |
Upgrade blockers first
Keep compiler, generated code, and runtime compatible
- Never run generated code against a runtime older than the compiler/plugin that
generated it, including patch-version mismatches.
- C++ and Rust require exact generated-code/runtime release matches. C++ does not
promise ABI stability even across minor or patch releases.
- Most other runtimes accept major
V generated code on runtime V and V+1,
plus older-minor generated code within the same major. Runtime V+2 is unsupported.
- Python's descriptor-based generated code from 3.20.0 onward has an extended window
through at least runtime 8.x.
- Do not load multiple major runtime versions into one process. Security fixes can
require both a runtime upgrade and regeneration even inside a nominal window.
Check language and build baselines
Before upgrading, enforce these relevant floors and build changes:
- C++ requires C++17 from release 30 onward.
- Python requires 3.9 from runtime 6.30 and 3.10 from runtime 7.34.
- PHP requires PHP 8.2 from runtime 5.34.
- Ruby 3.0 is unsupported from release 31; use Ruby 3.1 or newer.
- Protobuf 34 requires Bazel 8 and defaults dependency setup to Bzlmod.
- CMake no longer builds Protobuf's tests by default; explicitly enable them in CI
when test targets are required.
Replace removed reflection and runtime APIs
- Replace C++
Arena::CreateMessage with Arena::Create, Arena::GetArena with
value->GetArena(), and JsonOptions with JsonPrintOptions.
- Replace removed descriptor label accessors with semantic queries: repetition,
requiredness, and presence. Do not reconstruct those concepts from a legacy label.
- Python dynamic-message creation uses
message_factory.GetMessageClass() or
GetMessageClassesForFiles(); the old reflection/factory creation APIs are gone.
- Objective-C uses ordering-preserving
GPBUnknownFields, not
GPBUnknownFieldSet, and runtimes no longer support generated code older than 3.22.
- PHP uses
Google\Protobuf\Field\Kind, Google\Protobuf\Field\Cardinality, and
Google\Protobuf\RepeatedField; the underscored/internal types are gone.
Stop relying on unchecked repeated-field operations
C++ repeated-field reflection no longer exposes Reserve(). Range and index checks
now cover Get, ExtractSubrange, DeleteSubrange, UnsafeArenaExtractSubrange,
ReleaseLast, and SwapElements; invalid access can abort. Validate positions,
counts, and nonempty assumptions before calling them.
Edition migration quick reference
Edition 2024
- C++ string fields default to view-style APIs, and enum-name helpers can return
absl::string_view. Copy when ownership or null termination is required.
- Naming-style enforcement is strict by default. Fix names or explicitly request
legacy style where supported.
- Top-level symbols default to exported while nested symbols default to local;
use
export and local deliberately.
- Replace weak declarations with
import option; place option imports after normal
imports and put them in Bazel option_deps (Bazel 8+).
- Replace
ctype with features.(pb.cpp).string_type.
- Java generates classes in separate files by default.
nest_in_file_class replaces
java_multiple_files, and the default outer name is the camel-cased filename plus
Proto unless java_outer_classname overrides it.
Edition 2026 features available to schemas
- Go defaults to the Opaque API in Editions 2024 and 2026. Select
API_HYBRID for
a staged field-to-accessor migration or API_OPEN to preserve direct fields.
- C++ can opt into repeated-field proxy accessors with
features.(pb.cpp).repeated_type = PROXY; the default stays LEGACY.
- Go can keep, strip, or temporarily generate both forms of enum prefixes with
features.(pb.go).strip_enum_prefix.
- Enum values can set a custom JSON spelling with
(pb.enumvalue.json).string.
- C++ bindings can set a namespace independent of the proto package with
(pb.file.cpp).namespace.
- Remove
cc_api_version, cc_utf8_verification, and cc_enable_arenas from
Edition 2026 schemas.
C++ migration decisions
Borrowed strings
Descriptor names, MessageLite::GetTypeName, and
UnknownField::length_delimited return absl::string_view. Keep views only while
their source remains alive; copy to std::string for storage or C APIs. Never assume
data() is null-terminated.
Arenas and containers
Do not call the removed arena-taking constructors for RepeatedField,
RepeatedPtrField, or Map. Review copy/move assumptions around the chunked
RepeatedPtrField layout. Use Arena::Ptr and Arena::UniquePtr when explicit
smart-pointer forms fit arena-associated values. In debug/ASAN builds, clearing an
arena oneof invalidates and poisons the cleared message; later access is a real
use-after-free bug.
Debug and serialization output
Debug-string APIs redact debug_redact fields, add a randomized process prefix,
and are not parseable TextFormat. Use them only for logs. Use binary encoding for
serialization or an explicit TextFormat printer when parseable unredacted text is
intentionally required.
Reflection and presence
Treat cardinality, requiredness, optional-keyword state, oneof membership, and
presence as distinct questions:
- repetition:
isRepeated / is_repeated();
- requiredness:
isRequired / is_required();
- presence:
hasPresence / has_presence();
- proto3 optional keyword:
hasOptionalKeyword where still supported;
- real oneof membership:
getRealContainingOneof where applicable.
For Editions, singular fields no longer gain useful semantic distinctions from a
legacy label. PHP's broken hasOptionalKeyword() is gone in favor of
hasPresence(); Objective-C replaces -[GPBFieldDescriptor optional] with
!required && fieldType == GPBFieldTypeSingle.
Parser hardening checklist
When accepting untrusted input:
- set recursion controls for Python text format and account for recursion checks in
nested Python/upb messages, Java JSON
Any, and C# JSON well-known types;
- expect Ruby/PHP JSON numeric fields to reject nonnumeric strings;
- expect PHP JSON parsing to reject range errors, fractional values for integer
fields, duplicate oneof members, and non-string values for string fields;
- expect upb to reject malformed descriptor
syntax or edition values;
- expect C++
BinaryToJson to report failures while skipping malformed unknown
fields rather than silently succeeding.
Verification checklist
- Confirm all generated files come from the intended compiler/plugin release.
- Compile C++ with C++17 and warnings that surface newly
[[nodiscard]] results.
- Test reflection against repeated, required, present, proto3-optional, and oneof
fields rather than asserting legacy labels.
- Test invalid bounds and ownership paths in debug and sanitizer builds.
- Test JSON numeric limits, duplicate oneofs, non-finite numbers, deep nesting, and
text-format recursion limits.
- Check serialized ordering only when the API promises it; do not parse debug text.
- Use absolute
protoc file output paths.
- For CMake/Bazel changes, pin dependency and toolchain selection where reproducible
builds depend on a prior default.
1---2name: protobuf-knowledge-patch3description: Protocol Buffers4license: MIT5---678# Protocol Buffers Knowledge Patch910Use this skill when changing `.proto` schemas, upgrading `protoc` or a language11runtime, regenerating bindings, maintaining reflection code, or updating12Protobuf build rules. Inspect the repository's compiler, runtime, generated-code,13edition, language, and build-tool versions before applying advice.1415## Working method16171. Identify every `protoc`, generator/plugin, runtime, and checked-in generated-code18 version in the project. Do not infer compatibility from the shared release number19 alone because language packages prepend different majors.202. Read the declared syntax or edition and all imported feature/options files.21 Edition behavior can change generated APIs independently of runtime version.223. Match guidance to the project's actual release. Preserve older-version behavior23 when the project has not crossed the corresponding change.244. Regenerate bindings after compiler or edition changes. Treat generated files as25 outputs, then compile and test each consuming language.265. Exercise malformed input, recursion limits, presence/reflection behavior, arena27 ownership, and JSON/text round trips when those surfaces are involved.2829## Reference index3031| Reference | Topics |32| --- | --- |33| [Compatibility, Builds, and Releases](references/compatibility-builds-and-releases.md) | Gencode/runtime rules, release numbering and support, CMake, Bazel, `protoc`, toolchains |34| [C++ Runtime and Generated APIs](references/cpp-runtime-and-generated-apis.md) | C++17, arenas, repeated fields, descriptors, debug output, bounds checks, JSON, flags |35| [Editions, Schema, and Descriptors](references/editions-schema-and-descriptors.md) | Edition 2024/2026 features, visibility, imports, naming, descriptors, custom options |36| [Java, C#, and Objective-C](references/java-csharp-and-objective-c.md) | Java generation and parsing, C# packages and recursion, Objective-C runtime migration |37| [PHP, Ruby, and Rust](references/php-ruby-and-rust.md) | Baselines, strict parsing, reflection, generated typing, RBS, Rust views and traits |38| [Python Runtime and Generated APIs](references/python-runtime-and-generated-apis.md) | Python baselines, removed APIs, maps, descriptors, formatting, upb, NumPy and text parsing |3940## Upgrade blockers first4142### Keep compiler, generated code, and runtime compatible4344- Never run generated code against a runtime older than the compiler/plugin that45 generated it, including patch-version mismatches.46- C++ and Rust require exact generated-code/runtime release matches. C++ does not47 promise ABI stability even across minor or patch releases.48- Most other runtimes accept major `V` generated code on runtime `V` and `V+1`,49 plus older-minor generated code within the same major. Runtime `V+2` is unsupported.50- Python's descriptor-based generated code from 3.20.0 onward has an extended window51 through at least runtime 8.x.52- Do not load multiple major runtime versions into one process. Security fixes can53 require both a runtime upgrade and regeneration even inside a nominal window.5455### Check language and build baselines5657Before upgrading, enforce these relevant floors and build changes:5859- C++ requires C++17 from release 30 onward.60- Python requires 3.9 from runtime 6.30 and 3.10 from runtime 7.34.61- PHP requires PHP 8.2 from runtime 5.34.62- Ruby 3.0 is unsupported from release 31; use Ruby 3.1 or newer.63- Protobuf 34 requires Bazel 8 and defaults dependency setup to Bzlmod.64- CMake no longer builds Protobuf's tests by default; explicitly enable them in CI65 when test targets are required.6667### Replace removed reflection and runtime APIs6869- Replace C++ `Arena::CreateMessage` with `Arena::Create`, `Arena::GetArena` with70 `value->GetArena()`, and `JsonOptions` with `JsonPrintOptions`.71- Replace removed descriptor label accessors with semantic queries: repetition,72 requiredness, and presence. Do not reconstruct those concepts from a legacy label.73- Python dynamic-message creation uses `message_factory.GetMessageClass()` or74 `GetMessageClassesForFiles()`; the old reflection/factory creation APIs are gone.75- Objective-C uses ordering-preserving `GPBUnknownFields`, not76 `GPBUnknownFieldSet`, and runtimes no longer support generated code older than 3.22.77- PHP uses `Google\Protobuf\Field\Kind`, `Google\Protobuf\Field\Cardinality`, and78 `Google\Protobuf\RepeatedField`; the underscored/internal types are gone.7980### Stop relying on unchecked repeated-field operations8182C++ repeated-field reflection no longer exposes `Reserve()`. Range and index checks83now cover `Get`, `ExtractSubrange`, `DeleteSubrange`, `UnsafeArenaExtractSubrange`,84`ReleaseLast`, and `SwapElements`; invalid access can abort. Validate positions,85counts, and nonempty assumptions before calling them.8687## Edition migration quick reference8889### Edition 20249091- C++ string fields default to view-style APIs, and enum-name helpers can return92 `absl::string_view`. Copy when ownership or null termination is required.93- Naming-style enforcement is strict by default. Fix names or explicitly request94 legacy style where supported.95- Top-level symbols default to exported while nested symbols default to local;96 use `export` and `local` deliberately.97- Replace weak declarations with `import option`; place option imports after normal98 imports and put them in Bazel `option_deps` (Bazel 8+).99- Replace `ctype` with `features.(pb.cpp).string_type`.100- Java generates classes in separate files by default. `nest_in_file_class` replaces101 `java_multiple_files`, and the default outer name is the camel-cased filename plus102 `Proto` unless `java_outer_classname` overrides it.103104### Edition 2026 features available to schemas105106- Go defaults to the Opaque API in Editions 2024 and 2026. Select `API_HYBRID` for107 a staged field-to-accessor migration or `API_OPEN` to preserve direct fields.108- C++ can opt into repeated-field proxy accessors with109 `features.(pb.cpp).repeated_type = PROXY`; the default stays `LEGACY`.110- Go can keep, strip, or temporarily generate both forms of enum prefixes with111 `features.(pb.go).strip_enum_prefix`.112- Enum values can set a custom JSON spelling with `(pb.enumvalue.json).string`.113- C++ bindings can set a namespace independent of the proto package with114 `(pb.file.cpp).namespace`.115- Remove `cc_api_version`, `cc_utf8_verification`, and `cc_enable_arenas` from116 Edition 2026 schemas.117118## C++ migration decisions119120### Borrowed strings121122Descriptor names, `MessageLite::GetTypeName`, and123`UnknownField::length_delimited` return `absl::string_view`. Keep views only while124their source remains alive; copy to `std::string` for storage or C APIs. Never assume125`data()` is null-terminated.126127### Arenas and containers128129Do not call the removed arena-taking constructors for `RepeatedField`,130`RepeatedPtrField`, or `Map`. Review copy/move assumptions around the chunked131`RepeatedPtrField` layout. Use `Arena::Ptr` and `Arena::UniquePtr` when explicit132smart-pointer forms fit arena-associated values. In debug/ASAN builds, clearing an133arena oneof invalidates and poisons the cleared message; later access is a real134use-after-free bug.135136### Debug and serialization output137138Debug-string APIs redact `debug_redact` fields, add a randomized process prefix,139and are not parseable TextFormat. Use them only for logs. Use binary encoding for140serialization or an explicit TextFormat printer when parseable unredacted text is141intentionally required.142143## Reflection and presence144145Treat cardinality, requiredness, optional-keyword state, oneof membership, and146presence as distinct questions:147148- repetition: `isRepeated` / `is_repeated()`;149- requiredness: `isRequired` / `is_required()`;150- presence: `hasPresence` / `has_presence()`;151- proto3 optional keyword: `hasOptionalKeyword` where still supported;152- real oneof membership: `getRealContainingOneof` where applicable.153154For Editions, singular fields no longer gain useful semantic distinctions from a155legacy label. PHP's broken `hasOptionalKeyword()` is gone in favor of156`hasPresence()`; Objective-C replaces `-[GPBFieldDescriptor optional]` with157`!required && fieldType == GPBFieldTypeSingle`.158159## Parser hardening checklist160161When accepting untrusted input:162163- set recursion controls for Python text format and account for recursion checks in164 nested Python/upb messages, Java JSON `Any`, and C# JSON well-known types;165- expect Ruby/PHP JSON numeric fields to reject nonnumeric strings;166- expect PHP JSON parsing to reject range errors, fractional values for integer167 fields, duplicate oneof members, and non-string values for string fields;168- expect upb to reject malformed descriptor `syntax` or `edition` values;169- expect C++ `BinaryToJson` to report failures while skipping malformed unknown170 fields rather than silently succeeding.171172## Verification checklist173174- Confirm all generated files come from the intended compiler/plugin release.175- Compile C++ with C++17 and warnings that surface newly `[[nodiscard]]` results.176- Test reflection against repeated, required, present, proto3-optional, and oneof177 fields rather than asserting legacy labels.178- Test invalid bounds and ownership paths in debug and sanitizer builds.179- Test JSON numeric limits, duplicate oneofs, non-finite numbers, deep nesting, and180 text-format recursion limits.181- Check serialized ordering only when the API promises it; do not parse debug text.182- Use absolute `protoc` file output paths.183- For CMake/Bazel changes, pin dependency and toolchain selection where reproducible184 builds depend on a prior default.