grpc-ecosystem/grpc-gateway · error

%s: %q: %w

Error message

%s: %q: %w

What it means

This error wraps a json.Decoder failure while decoding the VALUE that follows a top-level key (merge.go:199-201). The key was read successfully, but streaming the value into a json.RawMessage failed — the value is malformed (e.g. truncated string, bad number, unclosed array) or the document ends prematurely. It is prefixed with both the input name and the key name (%q) so the offending field is immediately identifiable.

Source

Thrown at openapiv3-merge/internal/merge/merge.go:201

	tok, err := dec.Token()
	if err != nil {
		return nil, fmt.Errorf("%s: %w", in.Name, err)
	}
	if delim, ok := tok.(json.Delim); !ok || delim != '{' {
		return nil, fmt.Errorf("%s: expected JSON object at top level", in.Name)
	}
	for dec.More() {
		tok, err := dec.Token()
		if err != nil {
			return nil, fmt.Errorf("%s: %w", in.Name, err)
		}
		key, ok := tok.(string)
		if !ok {
			return nil, fmt.Errorf("%s: unexpected token %v", in.Name, tok)
		}
		var raw json.RawMessage
		if err := dec.Decode(&raw); err != nil {
			return nil, fmt.Errorf("%s: %q: %w", in.Name, key, err)
		}
		switch key {
		case "openapi":
			if err := json.Unmarshal(raw, &d.OpenAPI); err != nil {
				return nil, fmt.Errorf("%s: openapi: %w", in.Name, err)
			}
		case "info":
			d.Info = raw
		case "servers":
			d.Servers = raw
		case "paths":
			obj, err := decodeOrderedObject(raw)
			if err != nil {
				return nil, fmt.Errorf("%s: paths: %w", in.Name, err)
			}
			d.Paths = obj
		case "webhooks":
			obj, err := decodeOrderedObject(raw)

View on GitHub (pinned to a58a4436a3)

Solutions

  1. Look at the key named in the error message and inspect that field's value in the input file for syntax errors.
  2. Run json.Valid / a linter on the input to get the exact byte offset of the malformed value.
  3. Escape or remove raw placeholders and unescaped quotes inside string values before merging.
  4. Re-export or re-download the spec if the file appears truncated mid-value.
  5. For generated specs, fix the generator's JSON serialization instead of post-patching the output.

Example fix

// before
"info": {"description": "uses "quotes" unescaped"}  // invalid
// after
"info": {"description": "uses \"quotes\" unescaped"}
Defensive patterns

Strategy: validation

Validate before calling

func preValidate(name string, data []byte) error {
    if !json.Valid(data) {
        return fmt.Errorf("%s: a field value is malformed; run a JSON linter for the exact offset", name)
    }
    return nil
}

Try / catch

var syntaxErr *json.SyntaxError
if err != nil && errors.As(err, &syntaxErr) {
    // err message is "<name>: \"<key>\": ..." — jump straight to that key in the file
    log.Printf("fix the value of the named key; syntax error at offset %d", syntaxErr.Offset)
}

Prevention

When it happens

Trigger: Calling Merge with Input data where some top-level field's value is syntactically invalid JSON — e.g. "info": {unclosed..., a truncated paths value, or an unterminated string after "description": — causing dec.Decode(&raw) to fail for that key.

Common situations: File cut off mid-value by a failed upload/download; templating that left placeholders like {{...}} inside a value; unescaped quotes in a description string produced by a generator; an editor crash leaving a partially saved spec.

Related errors


AI-assisted analysis of grpc-ecosystem/grpc-gateway@a58a4436a3 (2026-09-02). Data as JSON: /api/errors/fe2517f13f2e9060. Report an issue: GitHub.