Byte order mark (BOM) breaks JSON.parse

ErrorJavaScript

SyntaxError: Unexpected token '', "{ "a": 1 }" is not valid JSON

Where it stops

{
  "a": 1
}

What it means

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.

Last verified on Chromium 145, Firefox 146 and WebKit 26; Python 3.14; Jackson 2.21 and 3.1; Spring 6.2 and 7.0.

It opens with the error marked. Nothing is uploaded. Find where the error is.

Usually
A Windows editor saved as "UTF-8 with BOM"Exported from a spreadsheetConcatenating files
Fix
Strip a leading U+FEFF before parsing: JSON.parse(text.replace(/^\uFEFF/, "")).

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.

broken.jsonFails to parse
{
  "a": 1
}
JSON.parse stops at line 1, column 1.

Line 1: delete the byte order mark.

fixed.jsonParses
{
  "a": 1
}
JSON.parse reads it.

How the message appears

Chrome and Node first, then Firefox, then Safari.

Console
  • SyntaxError: Unexpected token '', "{ "a": 1 }" is not valid JSONChrome and Node
  • SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON dataFirefox
  • SyntaxError: JSON Parse error: Unrecognized token ''Safari
  • SyntaxError: Unexpected token '', "{"a": 1}" is not valid JSON

What causes it

  1. 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.

  2. 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.

  3. 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

Notenew 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.

NoteConfirm it before hunting elsewhere: head -c 3 file.json | xxd prints efbb bf if a BOM is present.

NotePython names it outright. json.loads on a string that starts with a BOM raises json.decoder.JSONDecodeError: Unexpected UTF-8 BOM (decode using utf-8-sig): line 1 column 1 (char 0). The message gives the fix: decode the file with utf-8-sig.

NoteJava is different. Jackson skips a BOM when it reads bytes, so readTree(byte[]) parses the document, and so does Spring's converter. It fails only when the BOM is still in a String you decoded yourself: Unexpected character ('' (code 65279 / 0xfeff)): expected a valid value (JSON String, Number, Array, Object or token 'null', 'true' or 'false'). Jackson 2.21.2 and 3.1.4 behave the same.

What you put into the tools never leaves your browser. We count page views and keep them for 90 days. Accept and we also remember this browser, so a return visit is not counted as new. Privacy policy