{"record":{"id":"e5dda3c795b17e82","repo":"JanDeDobbeleer/oh-my-posh","slug":"unexpected-data-after-top-level-value","errorCode":null,"errorMessage":"unexpected data after top-level value","messagePattern":"unexpected data after top-level value","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/config/load.go","lineNumber":297,"sourceCode":"\t\tcfg.Format = YAML\n\t\tparseErr = yaml.Unmarshal(data, &cfg)\n\tcase JSONC, JSON:\n\t\tcfg.Format = JSON\n\n\t\tstr := text.StripJSONComments(string(data))\n\t\tdata = []byte(str)\n\n\t\tdecoder := json.NewDecoder(bytes.NewReader(data))\n\t\tparseErr = decoder.Decode(&cfg)\n\n\t\t// decoder.Decode only reads the first JSON value and, unlike\n\t\t// json.Unmarshal, never checks what follows it - a stray character\n\t\t// after the closing brace (a leftover paste, a misplaced comma) would\n\t\t// otherwise be silently ignored rather than reported as the parse\n\t\t// error it is.\n\t\tif parseErr == nil {\n\t\t\tif _, tokErr := decoder.Token(); tokErr != io.EOF {\n\t\t\t\tparseErr = fmt.Errorf(\"unexpected data after top-level value\")\n\t\t\t}\n\t\t}\n\tcase TOML, TML:\n\t\tcfg.Format = TOML\n\t\tparseErr = toml.Unmarshal(data, &cfg)\n\tdefault:\n\t\tlog.Errorf(\"unsupported config file format: %s\", cfg.Format)\n\t\treturn nil, ErrInvalidExtension\n\t}\n\n\tif parseErr != nil {\n\t\tlog.Errorf(\"failed to parse config: %v\", parseErr)\n\t\treturn nil, ErrParse\n\t}\n\n\tpopulatePresence(&cfg, data)\n\n\treturn &cfg, nil","sourceCodeStart":279,"sourceCodeEnd":315,"githubUrl":"https://github.com/JanDeDobbeleer/oh-my-posh/blob/0976794618c5ed95de0985dded50de1b4dc914cb/src/config/load.go#L279-L315","documentation":"ParseBytes decodes JSON/JSONC configs with json.Decoder.Decode, which by design reads only the FIRST top-level value. To catch what json.Unmarshal would reject, it then reads one more token and requires io.EOF; if any extra token follows the top-level JSON object, this error is raised (and ParseBytes ultimately returns ErrParse). It exists so stray trailing text is reported instead of silently ignored.","triggerScenarios":"Calling ParseBytes (or read/renderSVG which call it) with JSON bytes containing anything after the closing brace of the top-level object: a second concatenated object, a leftover fragment, a stray comma or character.","commonSituations":"Concatenating two theme JSON files by accident; a copy-paste leaving remnants of an old config after the final '}'; a template/script appending text after the JSON; editing tools leaving trailing garbage after a truncation.","solutions":["Inspect the JSON text after the final closing brace of the top-level object and delete everything there","If two JSON documents were concatenated, split them or merge them into one object","Validate the file with a strict JSON parser (jq .) - it reports 'trailing characters' at the same spot","Re-export or re-download the theme file if it was truncated/corrupted"],"exampleFix":"// before\n{ \"version\": 3 } { \"version\": 2 }\n// after\n{ \"version\": 3 }","handlingStrategy":"try-catch","validationCode":"func strictJSON(data []byte) error {\n\treturn json.Unmarshal(data, &map[string]any{}) // Unmarshal rejects trailing data\n}\n// or: jq empty config.json","typeGuard":null,"tryCatchPattern":"cfg, err := config.ParseBytes(config.JSON, raw)\nif err != nil {\n\tif errors.Is(err, config.ErrParse) {\n\t\t// locate trailing garbage: scan past the first balanced top-level object\n\t}\n\treturn err\n}","preventionTips":["Never concatenate JSON documents - merge objects instead","Diff edited theme files against the original to spot leftover fragments","Run jq (or a strict parser) over JSON configs in CI","Beware of tools that append text after JSON output"],"tags":["json","jsonc","parsing"],"backgroundTag":"trailing-data-after-json","analyzedSha":"0976794618c5ed95de0985dded50de1b4dc914cb","analyzedAt":"2026-08-31T23:41:19.708Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}