Byte order mark (BOM) breaks JSON.parse

A byte order mark is the character U+FEFF, written as the bytes EF BB BF in UTF-8. It renders as nothing at all, and RFC 8259 does not permit it at the start of a JSON document: so a file that looks flawless fails at position 0.

How the message appears

The same fault, worded by different engines. V8 (Chrome, Node, Edge) changed its format around Chrome 109, so older and newer runtimes disagree about the same document.

  • SyntaxError: Unexpected token '', "{"a": 1}" is not valid JSON
  • SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data

The fix

Strip a leading U+FEFF before parsing: JSON.parse(text.replace(/^\uFEFF/, "")). Better, re-save the file as UTF-8 without a BOM so the next reader does not have to.

Fails to parse
{
  "a": 1
}
Parses
{
  "a": 1
}

The example travels in the URL fragment, which browsers never send to a server.

What causes it

A Windows editor saved as "UTF-8 with BOM"

Notepad, older Visual Studio and several Windows PowerShell redirection paths add one by default. Everything downstream reads the file correctly except the JSON parser.

Exported from a spreadsheet

Excel writes a BOM when saving UTF-8 so that it can recognise its own files later. Anything converted from such an export inherits it.

Concatenating files

Joining several UTF-8 files can leave a BOM in the middle of the result, where it is even harder to spot than at the start.

Worth knowing

  • new TextDecoder("utf-8").decode(bytes) removes a leading BOM for you unless you pass { ignoreBOM: true }. Node’s fs.readFileSync(path, "utf8") does not: which is why the same file parses in a browser and fails in a script.
  • Confirm it before hunting elsewhere: head -c 3 file.json | xxd prints efbb bf if a BOM is present.

Written by Vishnu Shankar.

Your JSON and text are processed entirely in your browser and are never sent anywhere. To understand how the site is used we record each page view (the page, the referrer and an approximate location from your IP address) and keep it for 90 days. Nothing is stored on your device unless you choose it. Accept to also store a device identifier here and record your browser and device characteristics, which is what lets a return visit be told from a new one. Decline and nothing is sent at all. We respect Do Not Track. See our privacy policy.