JSON troubleshooting

"undefined" or "[object Object]" is not valid JSON

Fix JSON.parse calls that receive undefined or an already-parsed object. Learn when to parse, when to stringify and how to guard missing storage values.

What the error means

JSON.parse expects JSON text and converts its argument to a string. undefined becomes the text undefined, and a plain object normally becomes [object Object]. Neither text is JSON. This usually means a value is missing or the caller tried to parse data that a library already decoded. Other primitive arguments can parse after coercion, but relying on coercion hides the input contract.

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
JavaScript / V8, missing valueSyntaxError: "undefined" is not valid JSONJSON.parse(undefined)
Reproduced in Node.js 24.21.0
JavaScript / V8, object valueSyntaxError: "[object Object]" is not valid JSONJSON.parse({})
Reproduced in Node.js 24.21.0
Older V8Unexpected token u in JSON at position 0undefined converted to text
V8 8.0 message template with u and 0 substituted
Older V8Unexpected token o in JSON at position 1A plain object converted to [object Object]
V8 8.0 message template with o and 1 substituted
Pythonjson.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)json.loads("undefined"), a string rather than a Python dict
Reproduced in Python 3.14.4

Causes you can reproduce

The expected property is absent

An absent property returns undefined. Check where the text should have been assigned, including asynchronous code and changed response shapes.

JAVASCRIPT
const response = {};
JSON.parse(response.payload);

The response is already parsed

Response.json() returns the decoded JavaScript value. Use that value directly. Calling JSON.parse again converts an object to an unrelated string.

JAVASCRIPT
const data = { id: 1 }; // for example, await response.json()
JSON.parse(data);

Storage contains the literal text undefined

setItem converts values to strings. Writing an undefined value stores invalid JSON. A missing localStorage key returns null, which is a separate case.

JAVASCRIPT
localStorage.setItem('settings', undefined);
JSON.parse(localStorage.getItem('settings'));

How to fix it

  1. Inspect typeof value and the value immediately before the failing parse call.
  2. If the value is already an object, use it directly. If your contract requires text, require a string and reject a missing value.
  3. Serialize objects with JSON.stringify when writing storage or sending JSON text. Validate that the value exists before storing it.
  4. Guard missing localStorage keys explicitly. Choose defaults based on your application, then parse only the stored text.

Parse text once and guard storage

The storage check distinguishes a missing key from an existing malformed entry. Handle malformed stored text explicitly in your application rather than silently discarding it.

JAVASCRIPT
const value = { theme: 'dark' };
localStorage.setItem('settings', JSON.stringify(value));
const raw = localStorage.getItem('settings');
const settings = raw === null ? { theme: 'system' } : JSON.parse(raw);

function parseJsonText(text) {
  if (typeof text !== 'string') {
    throw new TypeError('Expected JSON text');
  }
  return JSON.parse(text);
}

Keep Python objects and JSON text separate

requests.Response.json() returns a decoded Python value. json.loads accepts JSON text, bytes or bytearray; use json.dumps to serialize a Python object.

PYTHON
import json
data = {"id": 1}
text = json.dumps(data)
restored = json.loads(text)
# For an HTTP response: data = r.json()
# Use data directly; do not call json.loads(data).

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 repair action, 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 undefined a JSON value?

No. JSON supports objects, arrays, strings, numbers, true, false and null. JavaScript undefined has no JSON literal.

Should I stringify an object and immediately parse it?

If you already have the object and need to read it, use it directly. Stringify when a storage or transport boundary requires JSON text. A stringify-and-parse round trip can lose unsupported values and change types.

Does a missing localStorage key cause this error?

A missing key returns null, and JSON.parse(null) returns null through string coercion. An entry containing the literal text undefined does cause this error. Check the actual stored value.

Why did JSON.stringify return undefined?

JSON.stringify(undefined) returns JavaScript undefined rather than a JSON string. It also returns undefined for a top-level function or symbol. Check that your input is serializable before storing the result.

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.