ErrLookupBackground articles › JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about

JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about

"Unexpected token in JSON at position", "not valid JSON", "failed to JSON decode", and similar JSON parse errors appear when a library calls a JSON parser on text that is not well-formed JSON — usually a hand-edited or truncated file. This article explains which layer produces these errors, why the message rarely names the real problem, and the common causes: trailing commas, single quotes, comments, BOMs, smart quotes, merge-conflict markers, and truncated documents.

Distilled from 84 documented records across 45 repositories.

Background

This family covers every point where a library hands text or bytes to a JSON parser — JSON.parse in JavaScript, json.Unmarshal in Go, phutil_json_decode in PHP, json.load in Python, serde_json in Rust, json_decode in PHP, or a bespoke hand-rolled scanner like Hibernate's StringJsonDocumentReader — and the parser rejects the input as syntactically invalid. The errors look very different on the surface: a Node SyntaxError naming a token and position, a Go wrapped error carrying the JSON offset, a Python JSONDecodeError with line/column, a PHP PhutilJSONParserException, or a generic app-level toast like 'json_format_failed'. Underneath they are all the same condition: the input is not a well-formed JSON document per RFC 8259.

The mechanisms differ in what happens next, and that shapes how the error reaches you. Some libraries fail hard: maigret raises ValueError('Problem parsing json contents from file ...'), Phabricator wraps PhutilJSONParserException in a proxy exception ('Failed to decode rule data.', 'Failed to JSON decode rule data!'), and Hadoop's DiskBalancer throws Result.MALFORMED_PLAN after the plan's sha1 already verified. Others degrade deliberately: CodeWhale's config loader downgrades a bad mcp.server_definitions value to a warning and continues with an empty server list, ECC's context-dir migration keeps going with default metadata, claude-mem resets a corrupt settings file to an empty object (silently dropping your stored keys on the next write) and treats an unparseable PID file as 'no live worker'. A few are defensive guards rather than thrown errors at all: AnotherRedisDesktopManager blocks the save with a localized toast before re-encoding msgpack, protobuf, or PHP-serialized values, and Grav distinguishes a file literally containing null (valid) from a real decode failure by checking json_last_error(). The severity is library-specific — the same underlying syntax problem can be fatal, a silent data loss, or a blocked save depending on which one you are holding.

A recurring theme is that well-behaved parsers point at the exact offending byte. V8's 'Unexpected token ... in JSON at position N', the Go wrapped 'parsing node file %q: %w' with its JSON offset, PHP's json_last_error_msg(), and Python's json.tool output all tell you where the document broke. But many wrappers hide this: Phabricator's generic messages carry the offset only in the wrapped exception trace, AnotherRedisDesktopManager's toasts append the message only as an improvement suggestion, and the CLI parsing the coordination JSON in ECC issues is deliberately terse about file contents ('contents were omitted') so sensitive data never reaches logs. When the message is generic, running the file through an independent validator — jq, python -m json.tool, node -e "JSON.parse(...)" — recovers the position. Also worth knowing: parsers disagree about what is 'almost JSON'. JSON5 object literals, single-quoted keys, trailing commas, and comments all parse fine in JavaScript tooling but are rejected by every strict parser in this family; UTF-8 BOMs (Grav, maigret, ECC), smart quotes from rich-text editors, and git conflict markers are other frequent invisible offenders.

A distinct sub-family deserves mention: embedded-asset parse failures. CodeWhale's 'bundled model catalog must parse' and llmfit's 'embedded use_case_benchmarks.json is invalid' are include_str!-compiled JSON parsed at runtime with an .expect — they panic not because anything is wrong on your machine, but because the shipped asset and the deserialization struct drifted apart at build time. Similarly, some records fire on structurally valid JSON with the wrong shape: grafana/k6 fails unmarshalling options that are an array or scalar instead of an object, Hibernate's reader rejects a bare top-level string (state NONE, quote illegal there), and tailscale's node-file reader enforces key prefixes like 'privkey:' and 'mkey:' as typed fields. So 'JSON parse error' can mean invalid syntax, or valid syntax that does not match the expected schema — check which before editing anything.

Common causes

What usually fixes it

Go deeper

Documented occurrences

…and 64 more across the corpus — use search.

Honest provenance: generated on 2026-08-28 from AI-assisted analysis of the linked records. See how records are made.