{"record":{"id":"40aa41d748614975","repo":"grpc-ecosystem/grpc-gateway","slug":"expected-json-object-got-v","errorCode":null,"errorMessage":"expected JSON object, got %v","messagePattern":"expected JSON object, got (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"openapiv3-merge/internal/merge/merge.go","lineNumber":560,"sourceCode":"\t}\n\tbuf.WriteByte('}')\n\treturn buf.Bytes(), nil\n}\n\n// decodeOrderedObject parses a JSON object into an orderedObject, preserving\n// the input's key order.\nfunc decodeOrderedObject(raw json.RawMessage) (*orderedObject, error) {\n\tif isJSONNull(raw) {\n\t\treturn newOrderedObject(), nil\n\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 {","sourceCodeStart":542,"sourceCodeEnd":578,"githubUrl":"https://github.com/grpc-ecosystem/grpc-gateway/blob/a58a4436a376a4bcc7d8f10c4d4f919a8438bba9/openapiv3-merge/internal/merge/merge.go#L542-L578","documentation":"decodeOrderedObject expects the raw JSON it decodes to start with a `{` delimiter, i.e. be a JSON object. When the first token is anything else (array, string, number, true/false/null, or an unexpected token), it fails with `expected JSON object, got <token>`. The merge pipeline uses this to parse sections that must be objects while preserving key order.","triggerScenarios":"Calling parse on input whose raw data does not begin with a JSON object — e.g. the file contains a JSON array `[...]`, a bare string, `null` (non-raw-null path), `123`, or trailing garbage — so the first decoded token is not '{'.","commonSituations":"Pointing openapiv3-merge at a file that is a JSON array or YAML file instead of a JSON object; an empty or truncated file producing a different token; a wrapper script emitting an envelope object around the spec.","solutions":["Ensure every input file is a single JSON object starting with `{` (run `jq . file.json` to check and normalize)","Convert YAML specs to JSON before merging (the tool parses JSON)","Check for BOM characters or leading whitespace/comments that shift the first token; strip them","Verify the file is not truncated or wrapped in an array by the producing tool"],"exampleFix":"// before: specs.json contains\n[{\"openapi\":\"3.0.0\",...}]\n// after: unwrap to a single object\n{\"openapi\":\"3.0.0\",...}","handlingStrategy":"validation","validationCode":"func ensureJSONObject(path string) error {\n    b, err := os.ReadFile(path)\n    if err != nil { return err }\n    dec := json.NewDecoder(bytes.NewReader(bytes.TrimPrefix(b, []byte(\"\\xef\\xbb\\xbf\"))))\n    tok, err := dec.Token()\n    if err != nil { return err }\n    if d, ok := tok.(json.Delim); !ok || d != '{' {\n        return fmt.Errorf(\"%s: top-level value is not a JSON object\", path)\n    }\n    return nil\n}\n// run for every input before merge.Merge","typeGuard":"func isJSONObject(b []byte) bool {\n    dec := json.NewDecoder(bytes.NewReader(b))\n    tok, err := dec.Token()\n    if err != nil { return false }\n    d, ok := tok.(json.Delim)\n    return ok && d == '{'\n}","tryCatchPattern":"if err := merge.Merge(inputs); err != nil {\n    if strings.Contains(err.Error(), \"expected JSON object\") {\n        return fmt.Errorf(\"an input file is not a JSON object; convert YAML or unwrap arrays first: %w\", err)\n    }\n    return err\n}","preventionTips":["Always feed openapiv3-merge JSON object files, never YAML or arrays","Convert YAML specs with a yaml-to-json step in your pipeline","Strip BOMs and trailing garbage when files pass through editors or scripts","Sanity-check inputs with `jq . file.json` in CI before merging"],"tags":["go","json","openapi","malformed-input"],"backgroundTag":"expected-json-object","analyzedSha":"a58a4436a376a4bcc7d8f10c4d4f919a8438bba9","analyzedAt":"2026-09-02T10:28:31.537Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T16:17:10.729Z"}