{"record":{"id":"78ca5804f45f61c9","repo":"grpc-ecosystem/grpc-gateway","slug":"expected-string-key-got-v","errorCode":null,"errorMessage":"expected string key, got %v","messagePattern":"expected string key, got (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"openapiv3-merge/internal/merge/merge.go","lineNumber":570,"sourceCode":"\t}\n\tdec := json.NewDecoder(bytes.NewReader(raw))\n\tdec.UseNumber()\n\ttok, err := dec.Token()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif d, ok := tok.(json.Delim); !ok || d != '{' {\n\t\treturn nil, fmt.Errorf(\"expected JSON object, got %v\", tok)\n\t}\n\tout := newOrderedObject()\n\tfor dec.More() {\n\t\ttok, err := dec.Token()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tk, ok := tok.(string)\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"expected string key, got %v\", tok)\n\t\t}\n\t\tvar v json.RawMessage\n\t\tif err := dec.Decode(&v); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tout.set(k, v)\n\t}\n\tif _, err := dec.Token(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n","sourceCodeStart":552,"sourceCodeEnd":583,"githubUrl":"https://github.com/grpc-ecosystem/grpc-gateway/blob/a58a4436a376a4bcc7d8f10c4d4f919a8438bba9/openapiv3-merge/internal/merge/merge.go#L552-L583","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Re-read the input file bytes (check encoding and for corruption) and validate with `jq .` before merging","Ensure you pass complete file contents, not sliced or partially decoded RawMessage, into the merge API","Re-export/regenerate the offending spec file"],"exampleFix":"// before: partially decoded fragment passed to parse\nraw := data[10:]\n// after: pass the full document\nraw := data\nmerge.Merge([]merge.Input{{Name: \"spec.json\", Data: raw}})","handlingStrategy":"validation","validationCode":"// Confirm inputs are well-formed JSON objects before invoking the merge pipeline\nfor _, f := range files {\n    b, err := os.ReadFile(f)\n    if err != nil { return err }\n    if !json.Valid(b) {\n        return fmt.Errorf(\"%s: not valid JSON\", f)\n    }\n}","typeGuard":null,"tryCatchPattern":"if err := merge.Merge(inputs); err != nil {\n    if strings.Contains(err.Error(), \"expected string key\") {\n        return fmt.Errorf(\"input JSON stream is corrupted; re-export the spec: %w\", err)\n    }\n    return err\n}","preventionTips":["Pass complete, unmodified file bytes to the merge API — never sliced RawMessage","Check files for binary corruption or encoding problems after transfers","Run json.Valid on all inputs before merging"],"tags":["go","json","malformed-input"],"backgroundTag":"invalid-json-key","analyzedSha":"a58a4436a376a4bcc7d8f10c4d4f919a8438bba9","analyzedAt":"2026-09-02T10:28:31.537Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T16:17:10.729Z"}