Skip to main content

Guides

Fix “Unexpected Token in JSON at Position 0”

CleanPastedText editorial · Updated

Text workbench

Try it on your text

Cleaning mode

Removes hidden characters and standardizes AI-style punctuation.

Try a real example

Each sample contains a problem you cannot see.

Original

Pasted text

0 chars · 0 words

Cleaned

Ready to copy

0 changes

Text stays in this browser

Same words · No AI rewriting · No content logging

Why does JSON.parse fail at position 0?

Because the file starts with a byte order mark (U+FEFF) before the opening { or [. A BOM is a single invisible code point some tools write at the start of a UTF-8 file to mark its encoding. It has no visible glyph, so the file looks completely normal in most editors — but a JSON parser reads character by character from position 0, hits the BOM first, and stops immediately because U+FEFF is not part of the JSON grammar.

This is almost always a one-character problem. The rest of the file is typically valid JSON; deleting the invisible first character fixes the parse without touching anything else. The trap is that you cannot select or delete what you cannot see, which is why this error confuses people far more than its actual cause deserves.

What the error looks like, by environment

The exact wording differs by JavaScript engine and language, but the trigger is the same invisible character in every case:

EnvironmentTypical error text
V8 (Chrome, Node.js)Unexpected token '[BOM]', "[BOM]{..." is not valid JSON
Older V8 versionsUnexpected token in JSON at position 0
SpiderMonkey (Firefox)JSON.parse: unexpected character at line 1 column 1
Python 3 (json module)json.decoder.JSONDecodeError: Unexpected UTF-8 BOM (decode using utf-8-sig)

Python's message is the friendliest of the four — it names the cause and the fix in the same line, which is a useful hint even if you hit this in JavaScript instead.

How do I fix it in JavaScript or Node.js?

Strip the BOM from the string before handing it to JSON.parse:

const raw = fs.readFileSync("data.json", "utf8");
const data = JSON.parse(raw.replace(/^\uFEFF/, ""));

fs.readFileSync(path, "utf8") does not strip a BOM on its own — it decodes the bytes and leaves U+FEFF as the first character of the resulting string, so the replace has to happen explicitly. The same fix applies to a string from fetch(url).then(r => r.text()) or a pasted API response: strip /^\uFEFF/ before parsing, not after.

How do I fix it in Python?

Open the file with the utf-8-sig codec instead of utf-8. It detects a leading BOM during decoding and discards it, so json.load never sees the invisible character:

import json

with open("data.json", encoding="utf-8-sig") as f:
    data = json.load(f)

For a string already in memory — for example the body of an HTTP response — re-decode it the same way: text.encode().decode("utf-8-sig") before passing it to json.loads. A long-standing CPython tracker issue asked for json.load to strip a BOM automatically; it was closed without that change, so utf-8-sig remains the documented workaround rather than a temporary one.

Where does the BOM come from in the first place?

Almost always from Windows-side tooling that writes UTF-8 with a BOM by default or by habit:

  • Windows Notepad added a BOM to every UTF-8 file it saved for years; Notepad in the Windows 10 May 2019 Update switched the default to UTF-8 without a BOM, but files saved by older Notepad versions, or by other editors that copied its old behavior, still carry one.
  • Excel's “CSV UTF-8” export writes a BOM on purpose, so that reopening the file in Excel displays accented characters correctly. A JSON file built from that export, or from a script that assumes the export is plain UTF-8, inherits the mark.
  • PowerShell's Out-File and > redirection have historically defaulted to UTF-8 with a BOM on Windows, so a JSON file generated by a PowerShell script or CI step can pick one up without anyone writing it explicitly.
  • Concatenation — joining a BOM-prefixed file with others, or prepending a header to an existing JSON file with a text tool that adds its own encoding marker.

Does the JSON spec allow a BOM?

It permits parsers to tolerate one, but does not require them to. RFC 8259, the JSON specification, is explicit on both sides of this: section 8.1 says implementations "MUST NOT add a byte order mark to the beginning of a networked-transmitted JSON text," while parsers "MAY ignore the presence of a byte order mark rather than treating it as an error, for the sake of interoperability." A MAY is optional by definition — it is exactly why V8, SpiderMonkey, and Python's json module all choose to reject a leading BOM instead of silently skipping it, even though the spec would allow either choice.

Finding a BOM you can't see

A byte order mark renders as nothing in a plain text view, which is exactly why it survives unnoticed until a parser rejects it. Paste the file's contents into the cleaner at the top of this page: it lists the byte order mark by name and code point in the "What changed" report, alongside any other invisible characters riding along — without uploading the text anywhere. The full invisible Unicode characters list covers U+FEFF and its neighbors in more depth, and the guide to sanitizing text for code, JSON, and CSV walks through the wider workflow of cleaning structured data safely before it reaches a parser. If the odd character showed up after a copy-paste rather than a file export, see why pasted text develops weird spacing for the more general version of this problem.

Common questions

Frequently asked questions

What does “Unexpected token in JSON at position 0” actually mean?

Position 0 is the very first character JSON.parse looked at, and it wasn't the { or [ a JSON document must start with. The near-universal cause is a byte order mark (U+FEFF) sitting invisibly before it — a leftover encoding marker from whatever editor, export tool, or HTTP response produced the file. The visible content of the file is usually completely valid JSON; only the invisible first character is wrong.

How do I remove a BOM from a JSON string in JavaScript?

Strip it before parsing: JSON.parse(text.replace(/^\uFEFF/, '')). In Node.js, fs.readFileSync(path, 'utf8') keeps the BOM in the string, so apply the same replace after reading. Some frameworks and bundlers strip BOMs automatically on import; a raw fs.readFileSync or fetch().then(r => r.text()) does not.

Why does Python's json.load raise “Unexpected UTF-8 BOM”?

Because Python's json module treats a leading U+FEFF as invalid input rather than silently discarding it — a long-standing, deliberately unchanged behavior (see bpo-21509/gh-65708 below). Open the file with encoding="utf-8-sig" instead of "utf-8"; that codec detects and discards a leading BOM during decoding, before json.load ever sees it.

Where do BOMs in JSON files actually come from?

Mainly Windows-side tooling: Notepad saved UTF-8 files with a BOM by default for years (this changed in Windows 10 May 2019 Update), Excel adds a BOM when you export UTF-8 CSV so the file opens with correct accents next time, and some PowerShell versions write a BOM by default when redirecting output to a file with Out-File or >. A JSON file built from any of those, or from concatenating a BOM-prefixed file with others, inherits the mark.

Does the JSON specification allow a byte order mark?

RFC 8259, the JSON standard, says implementations “MUST NOT add a byte order mark to the beginning of a networked-transmitted JSON text,” while parsers “MAY ignore the presence of a byte order mark rather than treating it as an error,” for interoperability. That MAY is doing the work here: it makes tolerance optional, not required, which is why V8, SpiderMonkey, and Python's json module all reject a leading BOM instead of silently skipping it.

How do I check whether a file has a BOM without a hex editor?

Paste its contents into a character-level checker like the one on this page: a byte order mark shows up as its own row — named, counted, and impossible to miss — even though it renders as nothing in a normal text view. VS Code also flags it: click the encoding indicator in the bottom-right status bar, and a UTF-8 file with a BOM is labeled “UTF-8 with BOM” rather than plain “UTF-8.”

Continue reading

Related guides & tools