grpc-ecosystem/grpc-gateway · error
failed to encode OpenAPI for %s: %w
Error message
failed to encode OpenAPI for %s: %w
What it means
In genopenapi's Generator.Generate, when the OpenAPI registry is in merged-output mode, all per-file OpenAPI docs are merged and encoded into a single file named by reg.GetMergeFileName(). If encodeOpenAPI fails (JSON/YAML marshaling of the merged swagger document), the error is wrapped as 'failed to encode OpenAPI for %s: %w'. The inner error identifies why the document could not be serialized.
Source
Thrown at protoc-gen-openapiv2/internal/genopenapi/generator.go:451
continue
}
if err != nil {
return nil, err
}
openapis = append(openapis, &wrapper{
fileName: file.GetName(),
swagger: swagger,
})
}
if g.reg.IsAllowMerge() {
targetOpenAPI := mergeTargetFile(openapis, g.reg.GetMergeFileName())
if !g.reg.IsPreserveRPCOrder() {
targetOpenAPI.swagger.sortPathsAlphabetically()
}
f, err := encodeOpenAPI(targetOpenAPI, g.format)
if err != nil {
return nil, fmt.Errorf("failed to encode OpenAPI for %s: %w", g.reg.GetMergeFileName(), err)
}
files = append(files, f)
if grpclog.V(1) {
grpclog.Infof("New OpenAPI file will emit")
}
} else {
for _, file := range openapis {
if !g.reg.IsPreserveRPCOrder() {
file.swagger.sortPathsAlphabetically()
}
f, err := encodeOpenAPI(file, g.format)
if err != nil {
return nil, fmt.Errorf("failed to encode OpenAPI for %s: %w", file.fileName, err)
}
files = append(files, f)
if grpclog.V(1) {
grpclog.Infof("New OpenAPI file will emit")
}View on GitHub (pinned to a58a4436a3)
Solutions
- Read the wrapped inner error to find the exact field/value that failed to marshal.
- Audit openapiv2 options in your protos for invalid extension values or unsupported types in custom fields.
- Try switching g.format (json vs yaml) to isolate whether the YAML or JSON encoder is at fault.
- Bisect by generating per-file output (disable merge) to find which input proto produces the bad content.
Example fix
// before: openapiv2_option with bad custom extension causing marshal failure
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { extensions: { key: "x-bad" value { struct_value { } } } };
// after: remove or fix the extension so it serializes
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { info: { title: "API" version: "1.0" } }; Defensive patterns
Strategy: try-catch
Validate before calling
// Validate generated OpenAPI options serialize cleanly before generation:
_, err := json.Marshal(mergedSwaggerDoc)
if err != nil { log.Fatalf("openapi document not serializable: %v", err) } Try / catch
if err := runProtocGenOpenAPIv2(); err != nil {
var encErr *EncodingError
if errors.As(err, &encErr) {
log.Fatalf("failed to encode OpenAPI for %s: %v", encErr.File, errors.Unwrap(err))
}
return err
} Prevention
- Keep openapiv2 options to documented field types only.
- Avoid ad-hoc extensions with exotic protobuf Value types.
- Test OpenAPI generation in CI so marshal breakages surface immediately.
When it happens
Trigger: Generate() with merge mode enabled (single output file configured) where encodeOpenAPI(targetOpenAPI, g.format) returns an error — typically json.MarshalIndent failures from invalid/unsupported values (e.g. NaN, malformed extension values) or YAML encoding problems in the merged document.
Common situations: Custom OpenAPI options or extensions inject values that don't serialize (invalid custom openapiv2 options); a broken plugin hook adds malformed fields; extremely deep nesting hitting marshal recursion issues.
Related errors
- unexpected number of yaml nodes
- security: %w
- unknown enum type %s
- unknown message type %s
- exceeded recursive count (%d) for query parameter %q
AI-assisted analysis of grpc-ecosystem/grpc-gateway@a58a4436a3 (2026-09-02).
Data as JSON: /api/errors/35a8588f81c7cc43.
Report an issue: GitHub.