grpc-ecosystem/grpc-gateway · error
%s: tags: %w
Error message
%s: tags: %w
What it means
This error is produced while parse() unmarshals the top-level `tags` array of an OpenAPI document into []json.RawMessage. It wraps the underlying encoding/json error with the input document's name, so you know which file had a malformed `tags` field. The library throws it because `tags` must be a JSON array; any other shape fails to unmarshal.
Source
Thrown at openapiv3-merge/internal/merge/merge.go:236
case "webhooks":
obj, err := decodeOrderedObject(raw)
if err != nil {
return nil, fmt.Errorf("%s: webhooks: %w", in.Name, err)
}
d.Webhooks = obj
case "components":
if !isJSONNull(raw) {
if err := json.Unmarshal(raw, d.Components); err != nil {
return nil, fmt.Errorf("%s: components: %w", in.Name, err)
}
}
case "security":
if err := json.Unmarshal(raw, &d.Security); err != nil {
return nil, fmt.Errorf("%s: security: %w", in.Name, err)
}
case "tags":
if err := json.Unmarshal(raw, &d.Tags); err != nil {
return nil, fmt.Errorf("%s: tags: %w", in.Name, err)
}
case "externalDocs":
d.ExternalDocs = raw
default:
d.extras.set(key, raw)
}
}
if _, err := dec.Token(); err != nil {
return nil, fmt.Errorf("%s: %w", in.Name, err)
}
if d.OpenAPI == "" {
return nil, fmt.Errorf("%s: missing required field \"openapi\"", in.Name)
}
if isJSONNull(d.Info) {
return nil, fmt.Errorf("%s: missing required field \"info\"", in.Name)
}
return d, nil
}View on GitHub (pinned to a58a4436a3)
Solutions
- Open the named input file and verify the top-level "tags" value is a JSON array of objects
- Run the file through a JSON linter/parser to pinpoint the syntax or type error
- Regenerate the document from its source (e.g. protoc) if it was produced by a generator
- Fix any templating or conversion step that rewrites the tags section
Example fix
// before
"tags": { "name": "pets" }
// after
"tags": [ { "name": "pets" } ] Defensive patterns
Strategy: validation
Validate before calling
func tagsIsArray(data []byte) error {
var doc map[string]json.RawMessage
if err := json.Unmarshal(data, &doc); err != nil { return err }
raw, ok := doc["tags"]
if !ok { return nil }
var tags []json.RawMessage
return json.Unmarshal(raw, &tags)
} Type guard
func isArray(raw json.RawMessage) bool {
var arr []json.RawMessage
return json.Unmarshal(raw, &arr) == nil
} Try / catch
merged, err := merge.Merge(inputs)
if err != nil {
var tagErr *merge.TagError
if errors.As(err, &tagErr) { /* handle malformed tags for tagErr.Name */ }
return fmt.Errorf("merge failed: %w", err)
} Prevention
- Lint every input with a strict OpenAPI 3.1 validator before merging
- Keep "tags" as an array of objects per the spec; never hand-edit generated JSON without re-validating
- Add a CI step that parses each spec with encoding/json or jq before merge jobs
When it happens
Trigger: Calling Merge with an Input whose Data contains a top-level "tags" key whose value is not a JSON array (e.g. "tags": {} or "tags": "foo"), or contains entries that are not JSON values decodable by encoding/json.
Common situations: Hand-edited OpenAPI files, a code generator emitting tags as an object instead of an array, templating/config substitution corrupting the JSON, or YAML-to-JSON conversion producing the wrong shape for tags.
Related errors
AI-assisted analysis of grpc-ecosystem/grpc-gateway@a58a4436a3 (2026-09-02).
Data as JSON: /api/errors/efab66ff4d8169ee.
Report an issue: GitHub.