grpc-ecosystem/grpc-gateway · error

expected string key, got %v

Error message

expected string key, got %v

What it means

While streaming tokens of a JSON object, decodeOrderedObject expects every key token to be a string (as JSON requires). A non-string token appearing where a key should be means the byte stream is not a well-formed JSON object (e.g. the value of the previous pair ran into the next key, or the buffer is corrupted). Under valid JSON this is nearly unreachable, but it guards the token stream.

Source

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

	}
	dec := json.NewDecoder(bytes.NewReader(raw))
	dec.UseNumber()
	tok, err := dec.Token()
	if err != nil {
		return nil, err
	}
	if d, ok := tok.(json.Delim); !ok || d != '{' {
		return nil, fmt.Errorf("expected JSON object, got %v", tok)
	}
	out := newOrderedObject()
	for dec.More() {
		tok, err := dec.Token()
		if err != nil {
			return nil, err
		}
		k, ok := tok.(string)
		if !ok {
			return nil, fmt.Errorf("expected string key, got %v", tok)
		}
		var v json.RawMessage
		if err := dec.Decode(&v); err != nil {
			return nil, err
		}
		out.set(k, v)
	}
	if _, err := dec.Token(); err != nil {
		return nil, err
	}
	return out, nil
}

View on GitHub (pinned to a58a4436a3)

Solutions

  1. Re-read the input file bytes (check encoding and for corruption) and validate with `jq .` before merging
  2. Ensure you pass complete file contents, not sliced or partially decoded RawMessage, into the merge API
  3. Re-export/regenerate the offending spec file

Example fix

// before: partially decoded fragment passed to parse
raw := data[10:]
// after: pass the full document
raw := data
merge.Merge([]merge.Input{{Name: "spec.json", Data: raw}})
Defensive patterns

Strategy: validation

Validate before calling

// Confirm inputs are well-formed JSON objects before invoking the merge pipeline
for _, f := range files {
    b, err := os.ReadFile(f)
    if err != nil { return err }
    if !json.Valid(b) {
        return fmt.Errorf("%s: not valid JSON", f)
    }
}

Try / catch

if err := merge.Merge(inputs); err != nil {
    if strings.Contains(err.Error(), "expected string key") {
        return fmt.Errorf("input JSON stream is corrupted; re-export the spec: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: decodeOrderedObject iterating keys via dec.More()/Token() encounters a token that is not a string — only possible with malformed JSON input that slipped past the initial '{' check, such as structurally corrupted raw bytes or a hand-crafted RawMessage passed to parse.

Common situations: Programmatic use of the internal parse function with corrupted or hand-built RawMessage input; binary corruption or wrong-encoding reads of the spec file in unusual setups.

Related errors


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