JSON troubleshooting

JSON.parse: unexpected character at line 1 column 1 of the JSON data

Decode the Firefox JSON.parse error: what the first character means, how line and column are counted, and the Chrome, Safari and Node wording for the same input.

What the error means

This is Firefox's wording for a JSON.parse failure. Line 1 column 1 means the very first character cannot start a JSON value. A value can only start with {, [, a double quote, a digit, a minus sign, or the first letter of true, false or null. So the text is usually not JSON at all: an HTML page, a PHP warning printed before the JSON, a plain-text error, the word undefined, or an invisible byte-order mark. Firefox counts lines and columns from 1, skips leading spaces and newlines, and reports the character it rejected.

Exact messages by runtime

Messages depend on the input and runtime version. Node and Python rows were reproduced locally. Other rows cite official documentation or source. Rows marked as fragments or templates preserve the source wording and are not complete exception transcripts. Browsers and libraries may append location details.

RuntimeMessageInput and evidence
FirefoxSyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON dataJSON.parse("<!DOCTYPE html>")
Source-derived from Firefox 158 JSONParser.cpp and its message template
Firefox, after a newlineSyntaxError: JSON.parse: unexpected character at line 2 column 1 of the JSON dataJSON.parse("\n<br />")
Source-derived: lines and columns are counted from 1 up to the rejected character
Firefox, a wordSyntaxError: JSON.parse: unexpected keyword at line 1 column 1 of the JSON dataJSON.parse("not found")
Source-derived: a word starting with t, f or n that is not true, false or null
Firefox, empty inputSyntaxError: JSON.parse: unexpected end of data at line 1 column 1 of the JSON dataJSON.parse("")
Source-derived from Firefox 158 JSONParser.cpp
Chrome, Edge, Node.js / V8SyntaxError: Unexpected token '<', "<!DOCTYPE html>" is not valid JSONJSON.parse("<!DOCTYPE html>")
Reproduced in Node.js 24.21.0
Safari / JavaScriptCoreJSON Parse error: Unrecognized token '<'The same input
JavaScriptCore source (WebKit tag webkitgtk-2.54.1) with the JSON Parse error prefix
V8, byte-order markSyntaxError: Unexpected token '', "{"a":1}" is not valid JSONJSON.parse("\uFEFF{\"a\":1}"); the quoted token is an invisible U+FEFF
Reproduced in Node.js 24.21.0
Python, byte-order markjson.decoder.JSONDecodeError: Unexpected UTF-8 BOM (decode using utf-8-sig): line 1 column 1 (char 0)json.loads('\ufeff{"a":1}')
Reproduced in Python 3.14.4

Causes you can reproduce

A < starts an HTML page or a PHP warning

A login page, an error page or a PHP notice printed with display_errors on starts with <. If a newline comes first, Firefox reports line 2 column 1 instead. The HTML-response guide shows how to find which request returned it.

JAVASCRIPT
const body = '<br />\n<b>Warning</b>: Undefined variable $id';
JSON.parse(body);
// Firefox: JSON.parse: unexpected character at line 1 column 1 of the JSON data

A letter starts plain text or undefined

Text such as Internal Server Error, or the string undefined, starts with a letter that cannot begin a value. A word starting with n, t or f, such as not found, gives unexpected keyword in Firefox.

JAVASCRIPT
JSON.parse('Internal Server Error');
// Node: Unexpected token 'I', "Internal S"... is not valid JSON

An invisible byte-order mark comes first

In Node.js, fs.readFileSync(path, "utf8") keeps a UTF-8 BOM as the character U+FEFF, and JSON.parse rejects it. In browsers, response.text() and response.json() remove a leading UTF-8 BOM, so this cause is rarer there.

JAVASCRIPT
import { readFileSync, writeFileSync } from 'node:fs';
writeFileSync('bom.json', '\uFEFF{"a":1}');
JSON.parse(readFileSync('bom.json', 'utf8'));
// SyntaxError: Unexpected token ... is not valid JSON (the token is U+FEFF)

How to fix it

  1. Look at the raw text. In DevTools, open the failing request in the Network panel and check its status, Content-Type and response body.
  2. Make invisible characters visible by logging JSON.stringify(text.slice(0, 80)). A byte-order mark shows as \ufeff, and HTML or a PHP warning shows its first tag.
  3. Use the first character: < means HTML, a letter means plain text or undefined, and \ufeff means a byte-order mark. An empty body gives unexpected end of data instead.
  4. Fix the source: the URL, the session, the server error or the PHP warning. If the column is greater than 1, the text starts as JSON and breaks later, so match the rest of the message to the related guides.

Read the body once and report what arrived

The error message then shows the first characters, with invisible ones escaped, instead of a bare position.

JAVASCRIPT
async function readJson(response) {
  const text = await response.text();
  if (!response.ok) {
    throw new Error(`HTTP ${response.status}: ${JSON.stringify(text.slice(0, 80))}`);
  }
  try {
    return JSON.parse(text);
  } catch (error) {
    throw new Error(`Expected JSON, got ${JSON.stringify(text.slice(0, 80))}`, { cause: error });
  }
}

Remove one byte-order mark from a file in Node.js

Strip only a leading U+FEFF, and only where you read the file.

JAVASCRIPT
import { readFileSync } from 'node:fs';
const raw = readFileSync('bom.json', 'utf8');
const text = raw.charCodeAt(0) === 0xfeff ? raw.slice(1) : raw;
console.log(JSON.parse(text)); // { a: 1 }

Find the exact position with Tool Kumar

  1. Copy the exact input text, including its original whitespace. For HTTP failures, copy the raw response body.
  2. Open the Tool Kumar JSON validator and paste the text. The document stays in your browser.
  3. Read the line and column in the error or fixable-issue banner. For an invalid document, select Go to error to move the caret to that location.
  4. Inspect the preceding token as well as the marked character. A parser marks where it discovered the problem; the missing quote, separator or value can start earlier.

Python character offsets are zero-based, while its displayed lines and columns are one-based. V8 string positions count UTF-16 code units; Go offsets count bytes read. Unicode text can make these offsets differ. Use the original text and its line/column context rather than copying an offset between runtimes.

Tool Kumar can classify some input as fixable loose JSON. Review a proposed change before using the JSON repair tool, then validate the result against your API contract. For empty input, inspect the producer or response because there is no character to select.

Frequently asked questions

Is this error specific to Firefox?

The wording is. Chrome, Edge and Node.js say Unexpected token followed by the character, and Safari says JSON Parse error: Unrecognized token. The input and the fix are the same.

Why line 1 column 1 when the response looks like JSON?

You may be looking at a different request, or the JSON is preceded by something you cannot see, such as a byte-order mark. Log JSON.stringify of the first characters of the exact text you pass to JSON.parse.

What does unexpected keyword mean?

The text starts with t, f or n, but the word is not true, false or null. A plain-text reply such as not found, or a typo such as nul, causes it.

What do the other Firefox JSON.parse messages mean?

expected property name or '}' points to quoting or a trailing comma. expected ',' or '}' after property value in object points to a missing comma or an unescaped quote. bad escaped character and bad control character in string literal point to string escaping. unexpected non-whitespace character after JSON data means extra data, and unexpected end of data means empty or truncated input.

Standards and diagnostic sources

RFC 8259 and ECMA-404 define JSON syntax. Diagnostic wording belongs to the parser. The table links each cited message to its source.