{"record":{"id":"415c29f6b9dd7c0c","repo":"gravitational/teleport","slug":"error-in-data-json-w","errorCode":null,"errorMessage":"error in data JSON: %w","messagePattern":"error in data JSON: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/accessgraph/apiclient/jsonmerge/jsonmerge.go","lineNumber":40,"sourceCode":"\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strconv\"\n)\n\n// JSONMerge merges patch into data using the object-merge behavior expected by\n// the generated union helpers.\nfunc JSONMerge(data, patch json.RawMessage) (json.RawMessage, error) {\n\tif data == nil {\n\t\tdata = []byte(`{}`)\n\t}\n\tif patch == nil {\n\t\tpatch = []byte(`{}`)\n\t}\n\n\tvar dataValue any\n\tif err := unmarshalJSON(data, &dataValue); err != nil {\n\t\treturn nil, fmt.Errorf(\"error in data JSON: %w\", err)\n\t}\n\n\tvar patchValue any\n\tif err := unmarshalJSON(patch, &patchValue); err != nil {\n\t\treturn nil, fmt.Errorf(\"error in patch JSON: %w\", err)\n\t}\n\n\tmerged, err := json.Marshal(mergeJSON(dataValue, patchValue))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error writing merged JSON: %w\", err)\n\t}\n\treturn merged, nil\n}\n\nfunc unmarshalJSON(data []byte, value any) error {\n\tdecoder := json.NewDecoder(bytes.NewReader(data))\n\tdecoder.UseNumber()\n\treturn decoder.Decode(value)","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/gravitational/teleport/blob/1283425b60ec5f60d509ba4c791183d452923ff7/lib/accessgraph/apiclient/jsonmerge/jsonmerge.go#L22-L58","documentation":"JSONMerge in the accessgraph API client validates its inputs by unmarshalling the `data` (base) document into a generic value before applying the patch. If `data` is not valid JSON (syntax error, empty input, trailing garbage), the underlying decode error is wrapped and returned. The merge never runs, so no partial output is produced.","triggerScenarios":"Calling JSONMerge (or any MergeActionTeleportProperties/MergeActionAWSProperties/MergeActionAzureProperties/MergeActionGitlabProperties/MergeActionOktaProperties which call it) with a `data` byte slice that is empty, malformed, truncated, or non-JSON content (e.g. YAML or a log line).","commonSituations":"Access Graph service responses or stored resources that failed to serialize earlier; reading a partially-written file; passing empty/nil-adjacent byte slices when a resource has no recorded properties.","solutions":["Validate the `data` payload with json.Valid(data) (or a test unmarshal) before calling JSONMerge; substitute `{}` when empty.","Inspect the wrapped decode error (offset/syntax message) to find the exact corrupt position in the input.","Trace upstream producers of `data` and fix the serialization/write path that emitted invalid JSON."],"exampleFix":"// before\nmerged, err := jsonmerge.JSONMerge(data, patch)\n// after\nif len(bytes.TrimSpace(data)) == 0 { data = []byte(\"{}\") }\nif !json.Valid(data) { return nil, fmt.Errorf(\"invalid base data JSON\") }\nmerged, err := jsonmerge.JSONMerge(data, patch)","handlingStrategy":"validation","validationCode":"if len(bytes.TrimSpace(data)) == 0 { data = []byte(\"{}\") }\nif !json.Valid(data) {\n\treturn nil, fmt.Errorf(\"refusing merge: data is not valid JSON\")\n}\nmerged, err := jsonmerge.JSONMerge(data, patch)","typeGuard":"func isValidJSON(b []byte) bool { return json.Valid(b) }","tryCatchPattern":"merged, err := jsonmerge.JSONMerge(data, patch)\nif err != nil {\n\tif strings.Contains(err.Error(), \"error in data JSON\") {\n\t\t// base document corrupt: log hex/offset, fall back to raw data without merge\n\t}\n}","preventionTips":["Sanitize empty payloads to `{}` before merging.","Log the first ~100 bytes of `data` on failure to spot YAML/log contamination.","Fix producers to write JSON atomically (temp file + rename) to avoid truncated reads."],"tags":["json","accessgraph","merge","validation"],"backgroundTag":"invalid-json-input","analyzedSha":"1283425b60ec5f60d509ba4c791183d452923ff7","analyzedAt":"2026-09-02T04:06:41.601Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}