grpc-ecosystem/grpc-gateway · error
openapi: %s declares %q but %s declares %q
Error message
openapi: %s declares %q but %s declares %q
What it means
Raised in mergeAll when two inputs declare different `openapi` version strings. The library's strict-merge policy requires all inputs to agree on the OpenAPI version; the first input's version establishes the expected value and any later contradicting input is rejected, naming both files and versions.
Source
Thrown at openapiv3-merge/internal/merge/merge.go:275
// values that later inputs must not contradict.
func mergeAll(docs []*document) (*document, error) {
first := docs[0]
out := &document{
name: "merged",
OpenAPI: first.OpenAPI,
Info: first.Info,
Servers: first.Servers,
Paths: newOrderedObject(),
Webhooks: newOrderedObject(),
Components: &components{},
ExternalDocs: first.ExternalDocs,
extras: newOrderedObject(),
}
seenTags := map[string]json.RawMessage{}
for _, d := range docs {
if d.OpenAPI != out.OpenAPI {
return nil, fmt.Errorf("openapi: %s declares %q but %s declares %q",
first.name, out.OpenAPI, d.name, d.OpenAPI)
}
// info/servers/externalDocs and unknown top-level keys are first-wins,
// silently. The first input's value is already in `out`; nothing to
// do for later inputs.
if err := mergeOrdered("paths", out.Paths, d.Paths, d.name); err != nil {
return nil, err
}
if err := mergeOrdered("webhooks", out.Webhooks, d.Webhooks, d.name); err != nil {
return nil, err
}
if err := mergeComponents(out.Components, d.Components, d.name); err != nil {
return nil, err
}
if err := mergeTags(out, seenTags, d); err != nil {
return nil, err
}
if err := mergeSecurity(out, d); err != nil {View on GitHub (pinned to a58a4436a3)
Solutions
- Regenerate or update the mismatching document so its "openapi" version matches the first input
- Pin all generator invocations to the same OpenAPI output version
- If the older file is 3.0.x, migrate it to 3.1 first, then merge
- Align dependency versions of the generator across packages
Example fix
// before (b.json) "openapi": "3.0.3" // after (b.json) "openapi": "3.1.0"
Defensive patterns
Strategy: validation
Validate before calling
func versionsMatch(inputs []merge.Input) error {
var first string
for _, in := range inputs {
var doc struct { OpenAPI string `json:"openapi"` }
if err := json.Unmarshal(in.Data, &doc); err != nil { return err }
if first == "" { first = doc.OpenAPI; continue }
if doc.OpenAPI != first {
return fmt.Errorf("version mismatch: %q vs %q in %s", first, doc.OpenAPI, in.Name)
}
}
return nil
} Try / catch
if err := versionsMatch(inputs); err != nil {
return fmt.Errorf("pre-merge check: %w", err)
}
merged, err := merge.Merge(inputs)
if err != nil { return err } Prevention
- Pin all protoc/generator invocations to the same OpenAPI output version
- Migrate older 3.0.x specs before adding them to the merge set
- Record the spec version in CI and fail fast on drift
- Keep generator dependencies versioned identically across packages
When it happens
Trigger: Calling Merge with inputs where docs[0].OpenAPI != docs[i].OpenAPI — e.g. one file says "3.1.0" and another says "3.0.3" or "3.1.1".
Common situations: Mixing documents generated by different generator versions (one pinned to 3.0.x, another upgraded to 3.1), partially migrated specs, or vendored third-party specs pinned to a different version.
Related errors
- %s.%q: %s redefines an entry with a different value
- components.%s.%q: %s redefines an entry with a different val
- tags[%q]: %w
- tags[%q]: %s redefines a tag with different metadata
- security: %s declares different requirements
AI-assisted analysis of grpc-ecosystem/grpc-gateway@a58a4436a3 (2026-09-02).
Data as JSON: /api/errors/f6b9befb6b96594e.
Report an issue: GitHub.