grpc-ecosystem/grpc-gateway · error
tags[%q]: %s redefines a tag with different metadata
Error message
tags[%q]: %s redefines a tag with different metadata
What it means
openapiv3-merge rejects specs where two inputs define a top-level tag with the same `name` but different metadata. Tags are deduplicated by name; duplicate names with differing metadata are an OpenAPI spec violation, so the merge fails rather than silently keeping one version. The message names the tag and which input file caused the conflict.
Source
Thrown at openapiv3-merge/internal/merge/merge.go:379
}
return nil
}
// mergeTags appends tags from src to out, deduplicating by `name`. Two tag
// entries sharing a name must declare identical metadata.
func mergeTags(out *document, seen map[string]json.RawMessage, src *document) error {
for _, raw := range src.Tags {
name, err := tagName(raw)
if err != nil {
return fmt.Errorf("%s: tags: %w", src.name, err)
}
if prev, ok := seen[name]; ok {
same, err := canonicalEqual(prev, raw)
if err != nil {
return fmt.Errorf("tags[%q]: %w", name, err)
}
if !same {
return fmt.Errorf("tags[%q]: %s redefines a tag with different metadata", name, src.name)
}
continue
}
seen[name] = raw
out.Tags = append(out.Tags, raw)
}
return nil
}
// mergeSecurity applies first-wins to the root `security` array. The first
// input to declare a non-empty `security` value establishes it; later
// inputs that declare a different non-empty value are an error. (The root
// `security` is a list of alternatives that apply across the whole API; if
// two generators disagree, silently keeping one would change what callers
// are allowed to do.)
func mergeSecurity(out, src *document) error {
if len(src.Security) == 0 {
return nilView on GitHub (pinned to a58a4436a3)
Solutions
- Make the duplicate tag metadata identical across all input files (copy one tag definition to the other spec)
- Remove the duplicate tag entry from one of the inputs and let the first-wins merge supply it
- Centralize shared tags in a single spec or generate all specs from one source of truth
- Check that all specs were generated with the same tool version so tag metadata matches
Example fix
// before: a.json {"tags":[{"name":"pets","description":"Pet API"}]}
// b.json {"tags":[{"name":"pets","description":"All pets"}]}
// after: make b.json match
// b.json {"tags":[{"name":"pets","description":"Pet API"}]} Defensive patterns
Strategy: validation
Validate before calling
func checkTagConsistency(files []string) error {
seen := map[string]string{}
for _, f := range files {
var doc struct {
Tags []struct {
Name string `json:"name"`
Description string `json:"description"`
} `json:"tags"`
}
b, _ := os.ReadFile(f)
if err := json.Unmarshal(b, &doc); err != nil { return err }
for _, t := range doc.Tags {
key := t.Name + "|" + t.Description
if prev, ok := seen[t.Name]; ok && prev != key {
return fmt.Errorf("tag %q differs between %s and a previous file", t.Name, f)
}
seen[t.Name] = key
}
}
return nil
} Type guard
func sameTagMetadata(a, b json.RawMessage) bool {
var x, y map[string]any
_ = json.Unmarshal(a, &x)
_ = json.Unmarshal(b, &y)
return reflect.DeepEqual(x, y)
} Try / catch
if err := merge.Merge(inputs); err != nil {
var conflictErr *ConflictError
if strings.Contains(err.Error(), "redefines a tag with different metadata") {
// surface which tag and files conflict; ask user to align metadata
}
return err
} Prevention
- Keep shared tag definitions in one spec and copy them verbatim into others
- Generate all specs from a single source of truth so tag metadata always matches
- Pin the same spec-generator version across teams
- Pre-check for duplicate tag names across inputs before merging
When it happens
Trigger: Running `openapiv3-merge a.json b.json` where both a and b contain a root `tags` entry with the same `name` but different values for any other field (description, externalDocs, extensions), and canonicalEqual reports them as not identical.
Common situations: Two teams independently document the same shared tag in their specs (e.g. different `description` for `pets`); one spec was regenerated with an updated tag description while the other was not; tools adding differing vendor extensions (x-*) to the same tag.
Related errors
- tags[%q]: %w
- security: %s declares different requirements
- %s: %w
- %s: expected JSON object at top level
- %s: %q: %w
AI-assisted analysis of grpc-ecosystem/grpc-gateway@a58a4436a3 (2026-09-02).
Data as JSON: /api/errors/567fbed18a317fa4.
Report an issue: GitHub.