Byte order mark (BOM) breaks JSON.parse
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.
{
"a": 1
}Line 1: delete the byte order mark.
{
"a": 1
}How the message appears
Chrome and Node first, then Firefox, then Safari.
- 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
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
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.