JanDeDobbeleer/oh-my-posh · error

unexpected data after top-level value

Error message

unexpected data after top-level value

What it means

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.

Solutions

  1. Inspect the JSON text after the final closing brace of the top-level object and delete everything there
  2. If two JSON documents were concatenated, split them or merge them into one object
  3. Validate the file with a strict JSON parser (jq .) - it reports 'trailing characters' at the same spot
  4. Re-export or re-download the theme file if it was truncated/corrupted

Example fix

// before
{ "version": 3 } { "version": 2 }
// after
{ "version": 3 }
Defensive patterns

Strategy: try-catch

Validate before calling

func strictJSON(data []byte) error {
	return json.Unmarshal(data, &map[string]any{}) // Unmarshal rejects trailing data
}
// or: jq empty config.json

Try / catch

cfg, err := config.ParseBytes(config.JSON, raw)
if err != nil {
	if errors.Is(err, config.ErrParse) {
		// locate trailing garbage: scan past the first balanced top-level object
	}
	return err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of JanDeDobbeleer/oh-my-posh@0976794618 (2026-08-31). Data as JSON: /api/errors/e5dda3c795b17e82. Report an issue: GitHub.

Appendix: source

Thrown at src/config/load.go:297

		cfg.Format = YAML
		parseErr = yaml.Unmarshal(data, &cfg)
	case JSONC, JSON:
		cfg.Format = JSON

		str := text.StripJSONComments(string(data))
		data = []byte(str)

		decoder := json.NewDecoder(bytes.NewReader(data))
		parseErr = decoder.Decode(&cfg)

		// decoder.Decode only reads the first JSON value and, unlike
		// json.Unmarshal, never checks what follows it - a stray character
		// after the closing brace (a leftover paste, a misplaced comma) would
		// otherwise be silently ignored rather than reported as the parse
		// error it is.
		if parseErr == nil {
			if _, tokErr := decoder.Token(); tokErr != io.EOF {
				parseErr = fmt.Errorf("unexpected data after top-level value")
			}
		}
	case TOML, TML:
		cfg.Format = TOML
		parseErr = toml.Unmarshal(data, &cfg)
	default:
		log.Errorf("unsupported config file format: %s", cfg.Format)
		return nil, ErrInvalidExtension
	}

	if parseErr != nil {
		log.Errorf("failed to parse config: %v", parseErr)
		return nil, ErrParse
	}

	populatePresence(&cfg, data)

	return &cfg, nil

View on GitHub (pinned to 0976794618)