Getting ChatGPT to Write Accurate gRPC Service Definitions Without Type Mismatches
You asked ChatGPT to write a gRPC service definition and it looked perfect β until protoc threw a type error, or your generated client tried to pass a string where the server expected an int64. This happens more than you'd think. ChatGPT has absorbed a lot of proto2 and proto3 syntax, but it conflates field types, forgets required message imports, and invents method signatures that don't match any real streaming pattern.
The fix isn't to stop using ChatGPT for proto files. It's to give it enough context that it can't guess wrong, and to validate its output before a single byte hits your pipeline.
What You'll Learn
- Why ChatGPT produces type mismatches in
.protofiles and what triggers them. - How to write a prompt that constrains the model to proto3 and your actual domain types.
- How to specify unary, server-streaming, client-streaming, and bidirectional methods without ambiguity.
- How to validate generated proto files with
protocand catch issues early. - The gotchas that waste hours: reserved field numbers, enum zero values, and nested message scope.
Prerequisites
You should have protoc installed and know what a .proto file is. Familiarity with at least one gRPC language runtime (Go, Python, Java, etc.) is assumed. You don't need to be a gRPC expert β if you were, you wouldn't need ChatGPT to draft your definitions.
How ChatGPT Gets gRPC Wrong
ChatGPT's training data includes a mix of proto2 and proto3 syntax, older gRPC tutorials, and community-written examples that sometimes have bugs. When you give it a vague prompt, it pattern-matches on whatever proto syntax it saw most, which is often not what you need.
The most common mistakes fall into a few categories:
- Wrong scalar types. It might use
int32where your domain needsint64, or reach forbyteswhen astringwas correct. These compile but cause silent data loss at runtime. - Missing or wrong imports.
google.protobuf.Timestamp,google.protobuf.Empty, and other well-known types require explicitimportstatements that ChatGPT often omits. - Incorrect streaming syntax. It sometimes writes
rpc GetStream(Request) returns (stream Response)when you actually wanted bidirectional streaming, or forgets thestreamkeyword entirely. - Proto2 artifacts in proto3 files.
requiredandoptionalfield labels,defaultvalues, andgroupsyntax are proto2 only. ChatGPT occasionally sneaks them in. - Enum zero-value omissions. Proto3 requires every enum to have a zero value (typically
UNKNOWN = 0). ChatGPT sometimes starts enums at 1 and skips 0 entirely.
These aren't hypothetical edge cases. If you've been using ChatGPT for GraphQL schema work, you've likely seen similar issues β the hallucination problem in schema generation applies just as much to proto files as it does to GraphQL SDL.
Structuring Your Prompt for Accurate Proto Files
The most effective approach is to treat your prompt like a spec document rather than a casual request. Give the model a template to fill in rather than asking it to invent one from scratch.
Here's a prompt pattern that works reliably:
You are writing a proto3 gRPC service definition.
Requirements:
- syntax = "proto3" at the top
- package name: payments
- Go package option: option go_package = "github.com/myorg/payments/proto;paymentspb";
- Import google/protobuf/timestamp.proto for any timestamp fields
- Import google/protobuf/empty.proto if any RPC returns nothing
Service: PaymentService
Messages:
1. CreatePaymentRequest
- user_id: string (UUID, not numeric)
- amount_cents: int64 (never int32 β amounts can exceed 2 billion)
- currency_code: string (ISO 4217, e.g. "USD")
- created_at: google.protobuf.Timestamp
2. CreatePaymentResponse
- payment_id: string
- status: PaymentStatus enum (UNKNOWN=0, PENDING=1, COMPLETED=2, FAILED=3)
RPC methods:
1. CreatePayment β unary β takes CreatePaymentRequest, returns CreatePaymentResponse
2. StreamPaymentUpdates β server-streaming β takes a payment_id string wrapped in a GetPaymentRequest message, returns a stream of PaymentUpdateEvent messages
Do not use proto2 syntax. Do not add fields I haven't listed. Add a comment above each message explaining its purpose.
Notice what this prompt does: it specifies the exact scalar type for every sensitive field and explains why (amounts can exceed 2 billion). It names the package, the Go package option, and the imports explicitly. It describes streaming methods in plain English with the words "unary" and "server-streaming" spelled out. And it ends with a prohibition against hallucinated extra fields.
Handling Streaming Methods Without Signature Errors
Streaming is where ChatGPT gets most creative in the wrong direction. There are four gRPC method types, and you need to name the one you want explicitly.
| Type | Proto Syntax | When to Use |
|---|---|---|
| Unary | rpc Method(Req) returns (Res) |
Single request, single response |
| Server-streaming | rpc Method(Req) returns (stream Res) |
One request, multiple responses over time |
| Client-streaming | rpc Method(stream Req) returns (Res) |
Multiple request chunks, one final response |
| Bidirectional | rpc Method(stream Req) returns (stream Res) |
Full-duplex β both sides stream freely |
In your prompt, always write the method name, type name (unary/server-streaming/client-streaming/bidirectional), and both the request and response message names. Don't say "streaming" without qualifying which direction. The model will guess, and it guesses wrong about half the time when the context is ambiguous.
If you're generating multiple RPCs in one go, list them as a numbered table in your prompt, not as prose. Structured input produces more structured output.
Specifying Field Types and Enums Correctly
The scalar type table in the proto3 spec is the ground truth ChatGPT should follow. When your prompt doesn't specify types, it interpolates from context, which is where mismatches appear. The most dangerous substitutions are:
int32instead ofint64for IDs, amounts, or timestamps in epoch milliseconds.stringinstead ofbytesfor binary blobs (or the reverse β usingbytesfor text fields).doubleinstead ofint64for monetary values (never use floating point for money in any schema).google.protobuf.Structfor arbitrary JSON, which then gets confused with a plainmessagetype.
For enums, always specify the zero value and what it means. Tell ChatGPT explicitly:
Enum: OrderStatus
- ORDER_STATUS_UNKNOWN = 0 (required zero value β use when status is not set)
- ORDER_STATUS_PENDING = 1
- ORDER_STATUS_SHIPPED = 2
- ORDER_STATUS_DELIVERED = 3
- ORDER_STATUS_CANCELLED = 4
Use the SCREAMING_SNAKE_CASE prefix convention: ORDER_STATUS_ before each value.
The prefix convention matters. Proto3 enum values are scoped to the package, not to the enum type. Two enums in the same file cannot share a value name. ChatGPT often misses this and produces names that collide when you have more than one enum in a file.
For well-known types, be explicit about the import. Tell the model: "Use google.protobuf.Timestamp for the created_at field and include import "google/protobuf/timestamp.proto"; at the top." Don't assume it'll add the import without being told.
Validating ChatGPT's Output Before It Hits Your Build
The fastest validation loop is to run protoc on whatever ChatGPT generates before you do anything else with it. Save the output to a temp file and compile it:
protoc \
--proto_path=. \
--proto_path=$(go env GOPATH)/pkg/mod/github.com/protocolbuffers/protobuf/src \
--go_out=. \
--go-grpc_out=. \
your_service.proto
If protoc exits clean, you've cleared the syntax bar. That's necessary but not sufficient. You also need to read the generated code and ask yourself three questions:
- Are the Go (or Python, Java, etc.) types what I actually expected? Open the generated file and check that
int64in the proto producedint64in Go and not something coerced to a smaller type. - Did any field numbers collide or get reused? If ChatGPT modified a definition you already had in production, check that it didn't reassign existing field numbers, which would break wire compatibility.
- Are the service method signatures reflected correctly in the generated server and client interfaces?
For teams shipping frequently, a CI step that compiles proto files on every PR catches regressions before they merge. Add protoc to your lint stage rather than relying on developers to remember to run it locally.
If you work with schemas that need backward compatibility, tools like buf breaking from the Buf CLI will tell you when a change would break existing clients. Run it after any AI-assisted edit:
buf breaking --against .git#branch=main
Common Pitfalls and How to Avoid Them
Reserved Field Numbers
If you're updating an existing service, tell ChatGPT which field numbers are already reserved. Otherwise it will happily reuse them. Include a comment block at the top of your prompt like: "Field numbers 1β5 are already in production. New fields must start at 6."
Nested Message Scope Confusion
ChatGPT sometimes generates nested messages (a message defined inside another message) when you didn't ask for them. This is valid proto3, but it can make generated code harder to use and import. If you don't want nested types, add: "Do not nest message definitions. All messages should be at the top level of the file."
One File vs. Multiple Files
ChatGPT tends to dump everything into a single .proto file, which works fine for small services but becomes a problem when you want to share message types across services. If you need a shared common.proto, specify that in your prompt and ask for the import statement explicitly.
The optional Keyword in Proto3
Proto3 re-introduced the optional keyword in a later revision to support field presence detection. ChatGPT sometimes adds it, sometimes doesn't, and occasionally confuses it with proto2 optional semantics. Be explicit: "Use optional only on fields where field presence (has-field detection) matters. All other fields should have no label."
Forgetting the Package Option for Your Language
The go_package, java_package, or py_package option is not optional in practice. Without it, generated code lands in inconvenient paths. Always include the relevant option in your prompt. This is the same kind of configuration drift issue you'd run into with OpenAPI specs β just as keeping OpenAPI specs aligned with your code requires explicit constraints, so does keeping proto options aligned with your repo structure.
Hallucinated Service Methods
ChatGPT sometimes adds "helpful" RPCs you didn't ask for, like a ListAll or Delete method. These look reasonable but they represent surface area you haven't designed or secured. End every prompt with: "Do not add any RPC methods I have not listed. Only generate what I've specified." This simple instruction cuts hallucinated additions significantly. It's the same discipline that helps when you're generating auth flows β the model will invent endpoints unless you explicitly forbid it.
Wrapping Up: Next Steps
ChatGPT is a fast first-draft tool for proto files, not a spec validator. When you give it vague requests, it fills the gaps with guesses. When you give it a structured spec with explicit field types, import requirements, and streaming semantics, it produces output that clears protoc on the first try most of the time.
Here are four concrete things to do before your next AI-assisted proto session:
- Write a proto prompt template for your project: package name, language options, well-known type imports, and field naming conventions pre-filled. Paste it into every ChatGPT session to anchor the model to your standards.
- Add
protocto your CI pipeline so every proto change is compiled automatically on every PR. Broken syntax should never reach a code review. - Install the Buf CLI and run
buf breakingagainst your main branch after any modification. It catches wire-compatibility breaks thatprotocwon't. - Keep a field number log per proto file. Document which numbers are in production so ChatGPT β or any human β can't accidentally reuse them in the next revision.
- Review enum zero values manually every time. This is the most consistently missed rule in AI-generated proto3 and the easiest to fix once you know to look for it.
If you find ChatGPT useful for API surface design more broadly, the same constraint-driven prompt approach applies when you're generating webhook handlers or designing any contract-first interface. The principle is the same: define the contract in your prompt so the model doesn't have to invent it.
Frequently Asked Questions
Why does ChatGPT use int32 instead of int64 in gRPC proto files?
ChatGPT defaults to int32 when your prompt doesn't specify the type, because it pattern-matches on common tutorial examples that use smaller integers. Always specify your scalar types explicitly in the prompt and explain why β for example, note that monetary amounts in cents can exceed the int32 maximum of about 2.1 billion.
How do I stop ChatGPT from adding gRPC methods I didn't ask for?
Add an explicit instruction at the end of your prompt: 'Do not add any RPC methods I have not listed.' Without this, the model often appends seemingly helpful methods like List or Delete that you haven't designed or secured. Treating the instruction as a hard constraint cuts hallucinated additions significantly.
Does ChatGPT understand the difference between proto2 and proto3 syntax?
It understands both, but it doesn't always apply them consistently. When you don't specify, it may mix proto2 constructs like required fields or default values into a proto3 file. Always begin your prompt with 'syntax = proto3' and add 'Do not use proto2 syntax' as an explicit rule.
What's the fastest way to validate a proto file generated by ChatGPT?
Run protoc on the generated file immediately β before editing or importing it. If protoc exits clean, you've passed syntax validation. Then open the generated code in your target language and confirm that field types match what you expected, especially for int64, timestamps, and enum values.
How should I handle well-known types like Timestamp in ChatGPT-generated proto files?
Specify the well-known type by its full name in your prompt and explicitly ask ChatGPT to include the correct import statement. For example, tell it to use google.protobuf.Timestamp for date fields and to add import 'google/protobuf/timestamp.proto' at the top of the file, because it routinely omits the import even when it uses the type correctly.
π€ Share this article
Sign in to saveRelated Articles
Comments (0)
No comments yet. Be the first!