AI Prompt Engineering

Getting ChatGPT to Write Accurate gRPC Service Definitions Without Type Mismatches

June 29, 2026 9 min read 4 views

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 .proto files 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 protoc and 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 int32 where your domain needs int64, or reach for bytes when a string was 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 explicit import statements that ChatGPT often omits.
  • Incorrect streaming syntax. It sometimes writes rpc GetStream(Request) returns (stream Response) when you actually wanted bidirectional streaming, or forgets the stream keyword entirely.
  • Proto2 artifacts in proto3 files. required and optional field labels, default values, and group syntax 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:

  • int32 instead of int64 for IDs, amounts, or timestamps in epoch milliseconds.
  • string instead of bytes for binary blobs (or the reverse β€” using bytes for text fields).
  • double instead of int64 for monetary values (never use floating point for money in any schema).
  • google.protobuf.Struct for arbitrary JSON, which then gets confused with a plain message type.

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:

  1. Are the Go (or Python, Java, etc.) types what I actually expected? Open the generated file and check that int64 in the proto produced int64 in Go and not something coerced to a smaller type.
  2. 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.
  3. 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:

  1. 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.
  2. Add protoc to your CI pipeline so every proto change is compiled automatically on every PR. Broken syntax should never reach a code review.
  3. Install the Buf CLI and run buf breaking against your main branch after any modification. It catches wire-compatibility breaks that protoc won't.
  4. 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.
  5. 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 save

Comments (0)

No comments yet. Be the first!

Leave a Comment

Sign in to comment with your profile.

πŸ“¬ Weekly Newsletter

Stay ahead of the curve

Get the best programming tutorials, data analytics tips, and tool reviews delivered to your inbox every week.

No spam. Unsubscribe anytime.