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.

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)

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.