Guides

Parsing and encoding errors: unexpected token, malformed input

"Unexpected token", "unexpected end of input", "invalid UTF-8", "malformed" — a parser rejected its input. The parser is almost never wrong; the interesting question is why the input isn't what you think it is, and the answer is usually one layer earlier than the error.

Read the message like a parser

"Unexpected token < in JSON at position 0"The input starts with < — it's HTML, not JSON. An error page, a login redirect, or a 404 came back where the API response should be. Log the raw body and the status code.
"Unexpected end of JSON input"Truncated: an empty response body, a connection cut mid-transfer, or a partially written file. Valid JSON doesn't stop early.
"Unexpected token" mid-documentGenuinely malformed at that spot: trailing commas, single quotes, unescaped newline in a string, NaN/undefined serialized by hand-built formatting, or two JSON objects concatenated.
"Invalid UTF-8" / mojibake (é, ’)Bytes in one encoding decoded as another — a Latin-1 database column read as UTF-8, a file saved in the wrong encoding, or binary data pushed through a text path.

Look at the actual bytes

curl -s https://api.example.com/data | head -c 200        # what actually comes back?
head -c 64 payload.json | xxd                              # BOM? binary? wrong file?

Most parse errors dissolve the moment you look at the raw input instead of the parsed result: the "JSON" that is an HTML error page, the file with a UTF-8 BOM (EF BB BF) that a strict parser rejects, the config saved as UTF-16 by a Windows editor. If the error names a position, count to it — the byte offset points at the exact problem in a way re-reading the code never will.

The layer-before rule

Treat a parse error as a symptom and find the producer: the API that returns HTML on error (fix: check content-type and status before parsing), the shell pipeline that mixed stderr into stdout, the template that string-concatenates JSON instead of using a serializer, the double-encoded payload (JSON.stringify applied twice — the tell is a quoted string full of \"). Hand-built serialization is the root cause behind a remarkable share of these; use the library serializer, always name the file and offset in your own error messages, and validate at the boundary so bad input fails where it enters, not three modules later.

Documented occurrences

153 analyzed errors across 42 libraries match this failure class. Each links to the thrown message, its source line, and documented fixes.

gohugoio/hugo

docker/cli

vercel/next.js

aio-libs/aiohttp

rails/rails

rust-lang/cargo

gofiber/fiber

pypa/pip

apache/kafka

mongodb/node-mongodb-native

…and 32 more libraries — search for your exact message.

Other failure classes