{"record":{"id":"14d4a608b456ea52","repo":"hashicorp/terraform","slug":"unexpected-token-after-valid-json-v","errorCode":null,"errorMessage":"unexpected token after valid JSON: %v","messagePattern":"unexpected token after valid JSON: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/command/jsonformat/structured/change.go","lineNumber":327,"sourceCode":"\tfor key, value := range values {\n\t\tout[key] = unmarshalGeneric(value)\n\t}\n\treturn out\n}\n\nfunc ParseJson(reader io.Reader) (interface{}, error) {\n\tdecoder := json.NewDecoder(reader)\n\tdecoder.UseNumber()\n\n\tvar jv interface{}\n\tif err := decoder.Decode(&jv); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// The JSON decoder should have consumed the entire input stream, so\n\t// we should be at EOF now.\n\tif token, err := decoder.Token(); err != io.EOF {\n\t\treturn nil, fmt.Errorf(\"unexpected token after valid JSON: %v\", token)\n\t}\n\n\treturn jv, nil\n}\n","sourceCodeStart":309,"sourceCodeEnd":332,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/command/jsonformat/structured/change.go#L309-L332","documentation":"Returned by ParseJson when the JSON decoder reads one valid JSON value from the io.Reader but then finds a non-EOF token immediately after. ParseJson expects the entire stream to be exactly one JSON document, so any trailing content is rejected.","triggerScenarios":"Passing a reader that contains concatenated JSON documents, a JSON value followed by trailing whitespace-garbage, or a stream that was not fully consumed because of a partial write. Also triggered by feeding a JSON array/object stream when a single scalar value was expected and vice versa in some callers.","commonSituations":"Piping output from a tool that emits newline-delimited JSON (NDJSON) into a caller expecting one document; buffer reuse leaving stale bytes; reading from a connection that delivered more than one framed message.","solutions":["Ensure the reader contains exactly one JSON document with no trailing tokens.","If the input is NDJSON or a stream, decode in a loop with decoder.Decode and handle each value separately instead of using ParseJson.","Trim or re-buffer the input to a single document before calling ParseJson.","Log the offending token (already included via %v) to identify which caller is producing extra content."],"exampleFix":"// before\nval, err := ParseJson(bytes.NewReader(raw)) // raw = `{...}{...}`\n// after\nfor dec := json.NewDecoder(bytes.NewReader(raw)); ; {\n    var v interface{}\n    if err := dec.Decode(&v); err == io.EOF { break } else if err != nil { return err }\n    _ = v\n}","handlingStrategy":"validation","validationCode":"// Ensure the reader contains exactly one JSON document before calling ParseJson.\nfunc singleDocument(raw []byte) error {\n    dec := json.NewDecoder(bytes.NewReader(raw))\n    var v interface{}\n    if err := dec.Decode(&v); err != nil {\n        return err\n    }\n    if _, err := dec.Token(); err != io.EOF {\n        return fmt.Errorf(\"expected EOF after one JSON document\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"jv, err := ParseJson(r)\nif err != nil {\n    if strings.Contains(err.Error(), \"unexpected token after valid JSON\") {\n        // switch to streaming decode for NDJSON inputs\n    }\n    return err\n}","preventionTips":["Do not feed NDJSON or concatenated documents into ParseJson; use a decode loop instead.","Reset/recreate buffers between uses to avoid stale trailing bytes.","Validate upstream that the source emits exactly one framed JSON document."],"tags":["go","terraform","json","encoding","jsonformat","streaming"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}