Protocol Buffers When You Need This Skill - Creating or editing files - Setting up or - Designing gRPC or Connect services - Adding protovalidate constraints - Troubleshooting buf lint or breaking change errors Core Workflow 1. Match Project Style Before writing proto code, review existing files in the project. Match conventions for naming, field ordering, structural patterns, validation, and documentation style. If none exists, ask the user what style should be used or an existing library to emulate. 2. Write Proto Code - Apply universal best practices from best practices.md - Add protovalid…

)\"\n }\n ];\n}\n```\n\n### Message-Level CEL\n\n```protobuf\nmessage DateRange {\n option (buf.validate.message).cel = {\n id: \"date_range_valid\"\n message: \"end_date must be after start_date\"\n expression: \"this.end_date > this.start_date\"\n };\n\n google.protobuf.Timestamp start_date = 1;\n google.protobuf.Timestamp end_date = 2;\n}\n\n// Use has() to check field presence for conditional validation\nmessage SearchFilters {\n option (buf.validate.message).cel = {\n id: \"conditional_requirement\"\n message: \"max_price required when min_price is set\"\n expression: \"!has(this.min_price) || has(this.max_price)\"\n };\n\n optional int32 min_price = 1;\n optional int32 max_price = 2;\n}\n```\n\n## CEL Extension Functions\n\n| Function | Description |\n|----------|-------------|\n| `isNan()`, `isInf()` | Test for NaN or infinity |\n| `isEmail()`, `isHostname()`, `isUri()`, `isUriRef()` | Format validation |\n| `isIp()`, `isIp(version)`, `isIpPrefix()` | IP/CIDR validation |\n| `isHostAndPort()` | Host:port validation |\n| `unique()` | Check list items are unique |\n| `this` | Current value being validated |\n| `now` | Current timestamp |\n\n## Common Patterns\n\nFor entity and reference message patterns, see [best_practices.md](best_practices.md#common-patterns).\n\n### Batch Request\n\n```protobuf\nmessage GetUsersRequest {\n repeated UserRef user_refs = 1 [\n (buf.validate.field).repeated.min_items = 1,\n (buf.validate.field).repeated.max_items = 250\n ];\n}\n```\n\n### Pagination\n\n```protobuf\nmessage ListRequest {\n uint32 page_size = 1 [(buf.validate.field).uint32.lte = 250];\n string page_token = 2 [(buf.validate.field).string.max_len = 4096];\n}\n```\n\nSee `assets/proto/example/v1/book_service.proto` for a complete List example with response, ordering, and filtering.\n\n### Cross-Reference Consistency\n\nWhen the same identifier appears in multiple messages, all validation constraints must be identical.\nSee [best_practices.md](best_practices.md#cross-reference-consistency) for details and examples.\n\n## Predefined Constraint Rules\n\nWhen the same validation logic is repeated across many fields, define reusable predefined constraints.\nExtension files must use proto2 or Editions syntax (not proto3).\nUse field numbers 50000-99999 for private schemas.\n\n```protobuf\n// extensions.proto (proto2 or Editions)\nextend buf.validate.StringRules {\n optional bool name = 50000 [(buf.validate.predefined).cel = {\n id: \"string.name\"\n message: \"name must be 1-100 characters\"\n expression: \"this.size() >= 1 && this.size() \u003c= 100\"\n }];\n}\n\n// Usage in proto3 files\nstring first_name = 1 [(buf.validate.field).string.(acme.common.v1.name) = true];\n```\n\nFor parameterized rules and more examples, see [protovalidate.com](https://protovalidate.com).\n\n## Runtime Validation\n\nProtovalidate annotations define constraints in the schema, but enforcement happens at runtime.\nAdd validation calls at your service boundaries—typically in RPC handlers or middleware.\n\n**Go:**\n```go\nimport protovalidate \"github.com/bufbuild/protovalidate-go\"\n\nvalidator, err := protovalidate.New()\nif err := validator.Validate(msg); err != nil {\n // Handle validation error\n}\n```\n\n**TypeScript:**\n```typescript\nimport { createValidator } from \"@bufbuild/protovalidate\";\n\nconst validator = createValidator();\nconst violations = validator.validate(msg);\n```\n\nFor Python, Java, and other languages, see [protovalidate.com](https://protovalidate.com).\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":13934,"content_sha256":"6baf948af505b4e1d639cea4ea5852ccb4b9c6b2fd555e63e27867b95ef77a76"},{"filename":"references/quick_reference.md","content":"# Protocol Buffers Quick Reference\n\nFast lookup for common patterns and rules. For details, see [best_practices.md](best_practices.md).\n\n## Field Numbering\n\n| Range | Encoding | Use For |\n|-------|----------|---------|\n| 1-15 | 1 byte | Frequently-set fields |\n| 16-2047 | 2 bytes | Standard fields |\n| 2048-262143 | 3 bytes | Rarely-set fields |\n| 19000-19999 | — | Reserved by protobuf (never use) |\n| 536870912+ | — | Reserved (never use) |\n\n**Rules:**\n- Never reuse field numbers (reserve deleted ones)\n- Avoid gaps in numbering\n- Order in definition doesn't affect wire format\n\n## Breaking Changes Checklist\n\n### Safe Changes\n- ✅ Add new fields\n- ✅ Add new enum values\n- ✅ Add new methods/services\n- ✅ Add new messages\n- ✅ Mark elements `deprecated`\n\n### Wire-Safe but Breaks Generated Code\n- ⚠️ Add `optional` to existing scalar fields\n- ⚠️ Remove fields (reserve number + name)\n- ⚠️ Remove enum values (reserve number + name)\n- ⚠️ Move fields into a oneof\n- ⚠️ Convert between compatible types (`int32` ↔ `int64`)\n\n### Breaking (Requires New Version)\n- ❌ Renumber fields\n- ❌ Change field types incompatibly\n- ❌ Rename fields (breaks JSON)\n- ❌ Change RPC signatures\n- ❌ Remove without reserving\n\n## Scalar Type Selection\n\n| Use Case | Type | Wire Encoding |\n|----------|------|---------------|\n| Regular integers | `int32`, `int64` | Varint (1-10 bytes) |\n| Frequently negative | `sint32`, `sint64` | ZigZag varint (efficient for small absolute values) |\n| Large values (>2²⁸) | `fixed32`, `fixed64` | Fixed 4/8 bytes |\n| Arbitrary bytes | `bytes` | Length-prefixed |\n| Text (UTF-8) | `string` | Length-prefixed |\n| Boolean | `bool` | Varint (1 byte) |\n| Decimals | `float`, `double` | Fixed 4/8 bytes |\n\n**Tips:**\n- Use signed types—many languages lack unsigned support\n- Use `int32/int64` by default; switch to `sint*` if values are often negative\n- Use `fixed*` only when values are consistently large\n\n### Size Optimization\n\n1. **Use field numbers 1-15** for frequently-set fields (1-byte tag)\n2. **Use enums over strings** for fixed sets of values\n3. **Consider `bytes` over `string`** if UTF-8 validation isn't needed\n4. **Nested messages** add 2+ bytes overhead per instance\n\n## Enum Template\n\n```protobuf\nenum Status {\n STATUS_UNSPECIFIED = 0; // Required: zero = unset\n STATUS_ACTIVE = 1;\n STATUS_INACTIVE = 2;\n}\n```\n\n**Rules:**\n- First value must be `0` and `*_UNSPECIFIED`\n- Prefix all values with enum name\n- Never remove values—reserve them\n\n## Message Template\n\n```protobuf\nmessage User {\n string id = 1;\n string email = 2;\n string display_name = 3;\n\n google.protobuf.Timestamp create_time = 10;\n google.protobuf.Timestamp update_time = 11;\n}\n```\n\n## Service Template\n\n```protobuf\nservice UserService {\n rpc GetUser(GetUserRequest) returns (User);\n rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);\n rpc CreateUser(CreateUserRequest) returns (User);\n rpc UpdateUser(UpdateUserRequest) returns (User);\n rpc DeleteUser(DeleteUserRequest) returns (google.protobuf.Empty);\n}\n```\n\n## Pagination Template\n\n```protobuf\nmessage ListUsersRequest {\n int32 page_size = 1;\n string page_token = 2;\n}\n\nmessage ListUsersResponse {\n repeated User users = 1;\n string next_page_token = 2;\n}\n```\n\n## Field Mask Template\n\n```protobuf\nimport \"google/protobuf/field_mask.proto\";\n\nmessage UpdateUserRequest {\n User user = 1;\n google.protobuf.FieldMask update_mask = 2;\n}\n```\n\n## Well-Known Types\n\n| Import | Type | Use For |\n|--------|------|---------|\n| `google/protobuf/timestamp.proto` | `Timestamp` | Points in time |\n| `google/protobuf/duration.proto` | `Duration` | Time spans |\n| `google/protobuf/field_mask.proto` | `FieldMask` | Partial updates |\n| `google/protobuf/empty.proto` | `Empty` | Empty responses |\n| `google/protobuf/struct.proto` | `Struct` | Dynamic JSON |\n| `google/protobuf/any.proto` | `Any` | Arbitrary messages |\n| `google/protobuf/wrappers.proto` | `*Value` | Nullable scalars |\n\n## Naming Conventions\n\n| Element | Style | Example |\n|---------|-------|---------|\n| Files | `lower_snake_case.proto` | `user_service.proto` |\n| Packages | `lower.dot.separated.v1` | `acme.user.v1` |\n| Messages | `PascalCase` | `UserProfile` |\n| Fields | `snake_case` | `display_name` |\n| Services | `PascalCase` | `UserService` |\n| RPCs | `PascalCase` | `GetUser` |\n| Enums | `PascalCase` | `UserStatus` |\n| Enum values | `UPPER_SNAKE_CASE` | `USER_STATUS_ACTIVE` |\n\n## Common Buf Commands\n\n```bash\nbuf lint # Lint protos\nbuf format -w # Format in place\nbuf breaking --against '.git#branch=main' # Check breaking\nbuf generate # Generate code\nbuf dep update # Update dependencies\n```\n\n## Reserved Keywords\n\nAvoid these in package names (cause issues in specific languages):\n\n| Keyword | Language |\n|---------|----------|\n| `internal` | Go |\n| `private` | Go |\n| `class` | Python, Java, C# |\n| `type` | TypeScript |\n| `import` | Multiple |\n| `package` | Multiple |\n\nFor Java, package names should avoid [JLS reserved keywords](https://docs.oracle.com/javase/specs/jls/se21/html/jls-3.html#jls-ReservedKeyword).\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":5185,"content_sha256":"da0ae3e4c46a9add81d411b106241c0c0df2cb469e02621102ec3aacfe9657bb"},{"filename":"references/review_checklist.md","content":"# Proto API Review Checklist\n\nUse this checklist when reviewing `.proto` files before an API stabilizes.\nIt captures common issues found during real-world proto API reviews.\n\nEvery field in a production API should have [protovalidate](protovalidate.md) constraints.\nA field without validation is a field that accepts anything.\n\n## Contents\n\n- [Enum Validation](#enum-validation)\n- [Oneof Validation](#oneof-validation)\n- [String Validation](#string-validation)\n- [Cross-Reference Consistency](#cross-reference-consistency)\n- [Pagination Validation](#pagination-validation)\n- [List Requests](#list-requests)\n- [Reserved Statements](#reserved-statements)\n- [Documentation Quality](#documentation-quality)\n\n---\n\n## Enum Validation\n\nEvery enum field should have appropriate validation constraints.\nThe correct constraints depend on how the enum is used.\n\nProto3 enums should have a zero value with the `_UNSPECIFIED` suffix (e.g., `STATUS_UNSPECIFIED = 0`).\nThis value represents \"not set\" and should have no semantic meaning.\nSee [Enums](best_practices.md#enums) for naming conventions.\n\n| Context | Constraints | Rationale |\n|---------|-------------|-----------|\n| Required enum field | `not_in = 0` + `defined_only = true` | Rejects the zero value and unknown values |\n| Optional enum field (zero value = \"not set\") | `defined_only = true` only | Zero value means \"not set\" or \"no preference\" |\n| Repeated enum items | `.repeated.items.enum.not_in = 0` + `.items.enum.defined_only = true` | Each item must be meaningful |\n\n**Common mistake:** Using only `defined_only = true` on a required enum field.\nThis allows the zero value through, which is rarely the intent.\n\n```protobuf\n// WRONG: allows the zero value through\nStatus status = 1 [(buf.validate.field).enum.defined_only = true];\n\n// RIGHT: rejects both the zero value and unknown values\nStatus status = 1 [\n (buf.validate.field).enum.not_in = 0,\n (buf.validate.field).enum.defined_only = true\n];\n\n// RIGHT: optional enum where zero means \"no preference\"\nStatus status_filter = 2 [(buf.validate.field).enum.defined_only = true];\n```\n\n## Oneof Validation\n\nAll oneofs representing a required choice should have `(buf.validate.oneof).required = true`.\n\n**Check these oneof types:**\n\n- **Lookup messages** (id-or-name lookups): always required\n- **Mutually exclusive operations** (add/update/delete in diff messages): always required\n- **Value types** (string/int/bool variants): required unless the oneof is intentionally optional\n\n```protobuf\n// Lookup by ID or email - always required\nmessage UserLookup {\n oneof value {\n option (buf.validate.oneof).required = true;\n string id = 1 [(buf.validate.field).string.uuid = true];\n string email = 2 [(buf.validate.field).string.email = true];\n }\n}\n```\n\n**Common mistake:** Omitting `required = true` on a lookup oneof.\nThis allows clients to send a lookup with no value set, which is never valid.\n\n## String Validation\n\nPrefer well-known standard constraints before reaching for `pattern`:\n\n| Constraint | Use For |\n|-----------|---------|\n| `.string.uuid` | UUIDs |\n| `.string.email` | Email addresses |\n| `.string.uri` / `.string.uri_ref` | URIs |\n| `.string.hostname` | Hostnames |\n| `.string.ip` / `.string.ipv4` / `.string.ipv6` | IP addresses |\n| `.string.address` | Hostname or IP |\n| `.string.host_and_port` | Host:port pairs |\n\nWhen no well-known constraint fits, use `pattern` for name/identifier fields:\n\n| Field Type | Pattern |\n|-----------|---------|\n| Lowercase with hyphens | `^[a-z0-9][a-z0-9-]*[a-z0-9] protobuf — Skillopedia |\n| Versioned label | `^[a-z0-9]([a-z0-9._-]*[a-z0-9])? protobuf — Skillopedia |\n| Programming identifier | `^[_a-zA-Z][_a-zA-Z0-9]* protobuf — Skillopedia |\n\n**Review items:**\n\n- [ ] String fields use well-known constraints where applicable before `pattern`\n- [ ] Name fields have appropriate length bounds (`min_len`, `max_len`)\n- [ ] Patterns are consistent with the character set the spec allows\n\n## Cross-Reference Consistency\n\nWhen the same identifier appears in multiple messages, all validation constraints must match.\n\n**Example problem:** `User.name` has `max_len = 32` but `TeamMember.user_name` (which references the same user name) has `max_len = 64`.\nA team member could reference a user name that is too long to be a valid user name.\n\n**Review items:**\n\n- [ ] Find all messages that reference the same identifier\n- [ ] Verify `max_len`, `min_len`, and `pattern` are identical across all references\n- [ ] Check companion messages for consistency\n\n## Pagination Validation\n\nPagination fields should have validation constraints.\n\n```protobuf\nuint32 page_size = 1 [(buf.validate.field).uint32.lte = 250];\nstring page_token = 2 [(buf.validate.field).string.max_len = 4096];\n```\n\n**Review items:**\n\n- [ ] `page_size` has an upper bound\n- [ ] `page_token` has a `max_len`\n- [ ] Response includes `next_page_token` with the same `max_len`\n- [ ] Response repeated field has `max_items` matching the `page_size` upper bound\n\n## List Requests\n\n**Review items:**\n\n- [ ] All List requests have pagination (`page_size` + `page_token`) with validation\n- [ ] Consider whether ordering and filtering are appropriate for the use case\n- [ ] Optional enum filter fields use `defined_only = true` only (zero value = \"no preference\")\n- [ ] Default behavior is documented\n\n## Reserved Statements\n\nWhen field numbers are skipped, always add `reserved` statements to prevent accidental reuse.\n\n**Review items:**\n\n- [ ] No gaps in field numbering without a `reserved` statement\n- [ ] Removed fields have both the field number and name reserved\n- [ ] Oneofs that start at field 2+ have field 1 reserved (or documented why)\n\n```protobuf\nmessage Example {\n reserved 1;\n\n // Type of the value.\n ValueType type = 2;\n\n oneof value {\n string string_value = 3;\n int64 int_value = 4;\n }\n}\n```\n\n## Documentation Quality\n\nDocumentation errors erode trust in the API and confuse implementers.\n\n**Review items:**\n\n- [ ] **Grammar: articles before vowels** — \"an Environment\" not \"a Environment\", \"an Image\" not \"a Image\"\n- [ ] **Consistent terminology** — pick one form and use it everywhere (e.g., \"shortname\" not sometimes \"short name\" and sometimes \"shortname\")\n- [ ] **No stale references** — check for references to old entity/concept names that have been renamed\n- [ ] **Implicit behavior is documented** — if an entity is auto-created, has reserved names, or has special behavior, document it on the relevant field\n- [ ] **Complete sentences** — comments should be full sentences with periods\n- [ ] **No truncated comments** — watch for comments cut off mid-sentence (e.g., \"typically by the\" with no completion)\n- [ ] **Typos** — common in large proto reviews: run spellcheck or read comments carefully\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":6704,"content_sha256":"4549bb2ba0b7b43ca25933af737e0e643c0e46fb203befcb43af25038600e883"},{"filename":"references/troubleshooting.md","content":"# Troubleshooting\n\nCommon errors when working with Protocol Buffers, with solutions.\n\n## Contents\n\n- [Linting Errors](#linting-errors)\n- [Breaking Change Errors](#breaking-change-errors)\n- [Code Generation Errors](#code-generation-errors)\n- [Import and Dependency Errors](#import-and-dependency-errors)\n- [Field and Schema Evolution](#field-and-schema-evolution)\n- [Runtime and Validation Errors](#runtime-and-validation-errors)\n\n---\n\n## Linting Errors\n\n### Quick Reference\n\n| Error | Problem | Fix |\n|-------|---------|-----|\n| ENUM_VALUE_PREFIX | `ACTIVE` | `STATUS_ACTIVE` (prefix with enum name) |\n| ENUM_ZERO_VALUE_SUFFIX | `STATUS_UNKNOWN` | `STATUS_UNSPECIFIED` |\n| PACKAGE_VERSION_SUFFIX | `acme.user` | `acme.user.v1` |\n| FIELD_LOWER_SNAKE_CASE | `userName` | `user_name` |\n| SERVICE_SUFFIX | `Users` | `UserService` |\n| RPC_REQUEST_RESPONSE_UNIQUE | Shared request types | Unique `{Method}Request` per RPC |\n| RPC_REQUEST_STANDARD_NAME | `UserRequest` | `GetUserRequest` |\n| COMMENT_* | Missing comments | Add `//` comments on public elements |\n| IMPORT_NO_WEAK/PUBLIC | `import public` | `import` (standard import) |\n\n### Ignoring Lint Rules\n\nFor legitimate exceptions, use comments (unless `disallow_comment_ignores: true`):\n\n```protobuf\n// buf:lint:ignore ENUM_VALUE_PREFIX\nenum LegacyStatus {\n UNKNOWN = 0;\n ACTIVE = 1;\n}\n```\n\nOr configure in `buf.yaml`:\n\n```yaml\nlint:\n ignore_only:\n ENUM_VALUE_PREFIX:\n - proto/legacy\n```\n\n## Breaking Change Errors\n\nBreaking changes are detected by `buf breaking --against \u003creference>`.\nThese errors indicate changes that would break existing clients.\n\n### FIELD_NO_DELETE\n\n```\n// Before\nmessage User {\n string id = 1;\n string email = 2;\n string name = 3;\n}\n\n// After - Error: field 3 deleted without reservation\nmessage User {\n string id = 1;\n string email = 2;\n}\n```\n\n**Fix:** Reserve deleted field numbers and names:\n\n```protobuf\nmessage User {\n reserved 3;\n reserved \"name\";\n\n string id = 1;\n string email = 2;\n}\n```\n\n### FIELD_SAME_NUMBER\n\n**Fix:** Never change field numbers. Create a new message version if restructuring is needed.\n\n### FIELD_SAME_TYPE\n\n**Fix:** Field types cannot change. Options:\n1. Add a new field with the correct type, deprecate the old one\n2. Create a new package version (v2)\n\n```protobuf\nmessage User {\n string user_id = 1 [deprecated = true];\n int64 user_id_v2 = 2;\n}\n```\n\n### FIELD_SAME_JSON_NAME\n\n**Fix:** Don't change JSON names. Use explicit `json_name` to preserve the original:\n\n```protobuf\nmessage User {\n string username = 1 [json_name = \"userName\"]; // Preserves JSON compatibility\n}\n```\n\n### ENUM_VALUE_NO_DELETE\n\n**Fix:** Reserve deleted enum values:\n\n```protobuf\nenum Status {\n reserved 2;\n reserved \"STATUS_INACTIVE\";\n\n STATUS_UNSPECIFIED = 0;\n STATUS_ACTIVE = 1;\n}\n```\n\n### RPC_NO_DELETE\n\n**Fix:** Don't remove RPCs. Deprecate instead:\n\n```protobuf\n// Deprecated: Use DeactivateUser instead.\nrpc DeleteUser(DeleteUserRequest) returns (DeleteUserResponse) {\n option deprecated = true;\n}\n```\n\n### MESSAGE_NO_DELETE / ENUM_NO_DELETE\n\n**Fix:** Don't remove messages or enums that may be in use. Deprecate first:\n\n```protobuf\noption deprecated = true;\nmessage LegacyUser { ... }\n```\n\n### FIELD_SAME_ONEOF\n\n**Fix:** Never move existing fields into a oneof. Add new fields to the oneof instead:\n\n```protobuf\nmessage Request {\n string id = 1; // Keep as-is\n oneof alternate_identifier {\n string name = 2;\n string email = 3;\n }\n}\n```\n\n### Breaking Change Categories\n\nBuf offers different strictness levels in `buf.yaml`:\n\n```yaml\nbreaking:\n use:\n - FILE # Strictest: file-level changes break\n - PACKAGE # Package-level: allows moving between files\n - WIRE_JSON # Wire + JSON encoding only\n - WIRE # Wire encoding only (most permissive)\n```\n\nFor internal APIs, `WIRE_JSON` may be sufficient. For public APIs, use `FILE`.\n\n## Code Generation Errors\n\n### Buf: Plugin Not Found\n\n**Fixes:**\n1. Check plugin name spelling in `buf.gen.yaml`\n2. For remote plugins, verify it exists on BSR: `buf.build/some/plugin`\n3. For local plugins, ensure it's in PATH\n\n### Buf: Remote Plugin Timeout\n\n**Fix:** Retry the command. Transient network issues are common.\n\n### Protoc: Plugin Not Found\n\n**Fixes:**\n1. Install the plugin\n2. Ensure GOBIN is in PATH:\n ```bash\n export PATH=\"$PATH:$(go env GOPATH)/bin\"\n ```\n\n### Go: \"go_package option required\"\n\n**Fixes:**\n\nWith buf (recommended): Enable managed mode in `buf.gen.yaml`:\n```yaml\nversion: v2\nmanaged:\n enabled: true\n override:\n - file_option: go_package_prefix\n value: github.com/yourorg/api/gen/go\n```\n\nWith protoc: Add `go_package` to each proto file:\n```protobuf\noption go_package = \"github.com/yourorg/api/gen/go/acme/user/v1;userv1\";\n```\n\n### Go: Conflicting Package Names\n\n**Fix:** Each proto package should map to a unique Go package.\nUse buf managed mode to handle this automatically.\n\n### Output Directory Issues\n\n**Fixes:**\n- Buf creates directories automatically—check write permissions\n- Protoc requires directories to exist: `mkdir -p gen/go`\n\n### Stale Generated Files\n\n**Fix:** Clean and regenerate:\n```bash\nrm -rf gen/\nbuf generate\n```\n\n## Import and Dependency Errors\n\n### Buf: Dependency Not Found\n\n```\nError: import \"buf/validate/validate.proto\": not found\n```\n\n**Fix:** Add dependency to `buf.yaml` and update:\n\n```yaml\ndeps:\n - buf.build/bufbuild/protovalidate\n```\n\n```bash\nbuf dep update\n```\n\n### Buf: Version Conflict\n\n**Fix:** Run `buf dep update` to resolve.\nIf persists, check for conflicting version pins across dependencies.\n\n### Protoc: Import Not Found\n\n**Fixes:**\n\n1. Add include path for well-known types:\n ```bash\n protoc -I /usr/local/include -I proto ...\n ```\n\n2. Find where WKTs are installed:\n ```bash\n ls /usr/local/include/google/protobuf/\n ```\n\n3. Vendor the dependency:\n ```bash\n git clone --depth 1 https://github.com/protocolbuffers/protobuf.git third_party/protobuf\n protoc -I third_party/protobuf/src -I proto ...\n ```\n\n### Protoc: googleapis Import Not Found\n\n**Fix:** Vendor googleapis or migrate to buf with BSR dependency:\n\n```bash\ngit clone --depth 1 https://github.com/googleapis/googleapis.git third_party/googleapis\nprotoc -I third_party/googleapis -I proto ...\n```\n\n### Circular Import\n\n**Fix:** Break the cycle by moving shared types to a common file:\n\n```\n# Before (circular)\na.proto imports b.proto\nb.proto imports a.proto\n\n# After (resolved)\ncommon.proto (shared types)\na.proto imports common.proto\nb.proto imports common.proto\n```\n\n## Field and Schema Evolution\n\n### Reserved Field Conflicts\n\n**Fix:** Use a different field number:\n\n```protobuf\nmessage User {\n reserved 3, 5;\n reserved \"old_name\";\n string name = 6; // Not 3 or 5\n}\n```\n\n### Field Number Reuse (Silent Corruption)\n\nReusing field numbers causes **silent data corruption**. Protobuf won't catch it:\n\n```protobuf\n// Version 1\nmessage User {\n string email = 3;\n}\n\n// Version 2 - DANGEROUS: reused field number with different type\nmessage User {\n int64 user_type = 3; // Silently corrupts data!\n}\n```\n\n**Prevention:**\n1. Always reserve deleted field numbers\n2. Run `buf breaking` in CI\n3. Never reuse field numbers, even if types match\n\n### Oneof Evolution\n\n**Moving fields into oneof breaks wire compatibility:**\n\n```protobuf\n// Before\nmessage Request {\n string id = 1;\n}\n\n// After - BREAKS clients\nmessage Request {\n oneof ref {\n string id = 1; // Same number, but now in oneof\n string name = 2;\n }\n}\n```\n\n**Adding fields to existing oneof is safe.**\n\n### Changing Field Optionality\n\nAdding `optional` keyword changes generated code but is wire-compatible:\n\n```protobuf\n// Before - cannot distinguish unset from \"\"\nstring nickname = 2;\n\n// After - can detect if set\noptional string nickname = 2;\n```\n\n**With protovalidate:** Changing required validation is a behavioral breaking change.\n\n### When to Create a New Version\n\nCreate a new package version (v1 → v2) when:\n- Multiple breaking changes are needed\n- The API shape is fundamentally changing\n- Field type changes are required\n- Deprecation period has ended\n\n```protobuf\n// acme/user/v1/user.proto - Keep for existing clients\npackage acme.user.v1;\n\n// acme/user/v2/user.proto - New version with breaking changes\npackage acme.user.v2;\n```\n\n## Runtime and Validation Errors\n\n### Protovalidate: Required Field Missing\n\n**Fix:** Ensure required fields are populated before sending.\n\nFor optional fields that shouldn't fail validation when empty:\n\n```protobuf\nstring url = 3 [\n (buf.validate.field).string.uri = true,\n (buf.validate.field).ignore = IGNORE_IF_DEFAULT_VALUE\n];\n```\n\n### Protovalidate: Pattern Mismatch\n\n**Fix:** Ensure input matches the regex. Check for:\n- Uppercase letters when lowercase required\n- Invalid characters\n- Missing required prefix/suffix\n\n### Unknown Fields Warning\n\n**Causes:**\n- Client using newer proto than server\n- Proto files out of sync between services\n\n**Fix:** Regenerate and redeploy to ensure consistent proto versions.\n\n### Message Size Limits\n\n**Fixes:**\n1. Increase limit if appropriate:\n ```go\n grpc.MaxRecvMsgSize(16 * 1024 * 1024)\n ```\n2. Use streaming for large payloads\n3. Paginate responses\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":9168,"content_sha256":"88f7765868cce4e2458a99e0c110bc1512d7a8b62befefb0c9fc32576dc6dbf5"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Protocol Buffers","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When You Need This Skill","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Creating or editing ","type":"text"},{"text":".proto","type":"text","marks":[{"type":"code_inline"}]},{"text":" files","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Setting up ","type":"text"},{"text":"buf.yaml","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"buf.gen.yaml","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Designing gRPC or Connect services","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Adding protovalidate constraints","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Troubleshooting buf lint or breaking change errors","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Core Workflow","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"1. Match Project Style","type":"text"}]},{"type":"paragraph","content":[{"text":"Before writing proto code, review existing ","type":"text"},{"text":".proto","type":"text","marks":[{"type":"code_inline"}]},{"text":" files in the project. Match conventions for naming, field ordering, structural patterns, validation, and documentation style. If none exists, ask the user what style should be used or an existing library to emulate.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"2. Write Proto Code","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Apply universal best practices from ","type":"text"},{"text":"best_practices.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/best_practices.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Add ","type":"text"},{"text":"protovalidate","type":"text","marks":[{"type":"link","attrs":{"href":"references/protovalidate.md","title":null}}]},{"text":" constraints to every field—this is not optional for production APIs","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"For service templates, see ","type":"text"},{"text":"assets/","type":"text","marks":[{"type":"link","attrs":{"href":"assets/","title":null}}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"3. Verify Changes","type":"text"}]},{"type":"paragraph","content":[{"text":"Always run after making changes:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"buf format -w && buf lint","type":"text"}]},{"type":"paragraph","content":[{"text":"Check for a Makefile first—many projects use ","type":"text"},{"text":"make lint","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"make format","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"Fix all errors before considering the change complete.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Quick Reference","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Task","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Reference","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Field types, enums, oneofs, maps","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"quick_reference.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/quick_reference.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Schema evolution, breaking changes","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"best_practices.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/best_practices.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Validation constraints","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"protovalidate.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/protovalidate.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Complete service examples","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"examples.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/examples.md","title":null}}]},{"text":", ","type":"text"},{"text":"assets/","type":"text","marks":[{"type":"link","attrs":{"href":"assets/","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"buf CLI, buf.yaml, buf.gen.yaml","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"buf_toolchain.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/buf_toolchain.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Migrating from protoc","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"migration.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/migration.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Lint errors, common issues","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"troubleshooting.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/troubleshooting.md","title":null}}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Proto API review checklist","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"review_checklist.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/review_checklist.md","title":null}}]}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Project Setup","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"New Project","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Create directory structure:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"proto/\n├── buf.yaml\n├── buf.gen.yaml\n└── company/\n └── domain/\n └── v1/\n └── service.proto","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"assets/buf.yaml","type":"text","marks":[{"type":"code_inline"}]},{"text":" as starting point","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Add ","type":"text"},{"text":"buf.build/bufbuild/protovalidate","type":"text","marks":[{"type":"code_inline"}]},{"text":" as a dependency in ","type":"text"},{"text":"buf.yaml","type":"text","marks":[{"type":"code_inline"}]},{"text":" and run ","type":"text"},{"text":"buf dep update","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"assets/buf.gen.*.yaml","type":"text","marks":[{"type":"code_inline"}]},{"text":" for code generation config","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Code Generation Templates","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Template","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Use For","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"buf.gen.go.yaml","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Go with gRPC","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"buf.gen.go-connect.yaml","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Go with Connect","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"buf.gen.ts.yaml","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"TypeScript with Connect","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"buf.gen.python.yaml","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Python with gRPC","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"buf.gen.java.yaml","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Java with gRPC","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Proto File Templates","type":"text"}]},{"type":"paragraph","content":[{"text":"Located in ","type":"text"},{"text":"assets/proto/example/v1/","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Template","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Description","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"book.proto","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Entity message, BookRef oneof, enum","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"book_service.proto","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Full CRUD with batch ops, pagination, ordering","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Common Tasks","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Add a new field","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use next sequential field number","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Add ","type":"text"},{"text":"protovalidate","type":"text","marks":[{"type":"link","attrs":{"href":"references/protovalidate.md","title":null}}]},{"text":" constraints: every field should have validation appropriate to its type (format validators, length bounds, numeric ranges, enum constraints, etc.)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Document the field","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Run ","type":"text"},{"text":"buf format -w && buf lint","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Remove a field","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Reserve the field number AND name:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"protobuf"},"content":[{"text":"reserved 4;\nreserved \"old_field_name\";","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Run ","type":"text"},{"text":"buf breaking --against '.git#branch=main'","type":"text","marks":[{"type":"code_inline"}]},{"text":" to verify","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Add protovalidate constraints","type":"text"}]},{"type":"paragraph","content":[{"text":"Every field in a production API should have appropriate validation. See ","type":"text"},{"text":"protovalidate.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/protovalidate.md","title":null}}]},{"text":" for the full reference.","type":"text"}]},{"type":"paragraph","content":[{"text":"Common constraints:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"String formats: ","type":"text"},{"text":".string.uuid","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":".string.email","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":".string.uri","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":".string.pattern","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"String bounds: ","type":"text"},{"text":".string.min_len","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":".string.max_len","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Numeric bounds: ","type":"text"},{"text":".int32.gte","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":".uint32.lte","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Enum validation: ","type":"text"},{"text":".enum.defined_only","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":".enum.not_in = 0","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Repeated bounds: ","type":"text"},{"text":".repeated.min_items","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":".repeated.max_items","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Required fields: ","type":"text"},{"text":"(buf.validate.field).required = true","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Oneof required: ","type":"text"},{"text":"(buf.validate.oneof).required = true","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Verification Checklist","type":"text"}]},{"type":"paragraph","content":[{"text":"After making changes:","type":"text"}]},{"type":"checkbox_list","attrs":{"id":null},"content":[{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Every field has appropriate protovalidate constraints","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"buf format -w","type":"text","marks":[{"type":"code_inline"}]},{"text":" (apply formatting)","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"buf lint","type":"text","marks":[{"type":"code_inline"}]},{"text":" (check style rules)","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"buf breaking --against '.git#branch=main'","type":"text","marks":[{"type":"code_inline"}]},{"text":" (if modifying existing schemas)","type":"text"}]}]}]}]},"metadata":{"date":"2026-11-02","name":"protobuf","author":"@skillopedia","source":{"stars":20,"repo_name":"claude-plugins","origin_url":"https://github.com/bufbuild/claude-plugins/blob/HEAD/plugins/protobuf/skills/protobuf/SKILL.md","repo_owner":"bufbuild","body_sha256":"8b09f409f34a024a37c582fdbe81fb12740ea8a9d446becc8f8c7550d57e11ee","cluster_key":"3d365d7adc1dfe6b497cbfb39e227fd23d0b0878e32b155bba8d23cb88fe8fa6","clean_bundle":{"format":"clean-skill-bundle-v1","source":"bufbuild/claude-plugins/plugins/protobuf/skills/protobuf/SKILL.md","text_bytes":74262,"source_commit":"c4766e5fff1b52283f38bd61122103efe58c60fd","skills_sh_rank":18512,"attachment_count":16,"text_attachments":16,"attachment_storage":"skill_attachments","binary_attachments":0},"cluster_size":1,"skill_md_path":"plugins/protobuf/skills/protobuf/SKILL.md","import_metadata":{"date":"2026-11-02","author":"@skillopedia","version":"v1","category":"software-engineering","category_label":"Engineering"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"software-engineering","import_tag":"skills-sh-top20k-2026-11","description":"Use when working with Protocol Buffer (.proto) files, buf.yaml, buf.gen.yaml, or buf.lock. Covers proto design, buf CLI, gRPC/Connect services, protovalidate constraints, schema evolution, and troubleshooting lint/breaking errors.","filePatterns":["**/*.proto","**/buf.yaml","**/buf.*.yaml","**/buf.gen.yaml","**/buf.gen.*.yaml","**/buf.lock"]}},"renderedAt":1793666220719}

Protocol Buffers When You Need This Skill - Creating or editing files - Setting up or - Designing gRPC or Connect services - Adding protovalidate constraints - Troubleshooting buf lint or breaking change errors Core Workflow 1. Match Project Style Before writing proto code, review existing files in the project. Match conventions for naming, field ordering, structural patterns, validation, and documentation style. If none exists, ask the user what style should be used or an existing library to emulate. 2. Write Proto Code - Apply universal best practices from best practices.md - Add protovalid…