← All articles

Unexpected Token o in JSON at Position 1: Causes and Fix

That lowercase "o" is the second character of "[object Object]". You passed a JavaScript object to JSON.parse() instead of a string. Here's every variant of this mistake and the one-line fix for each.

SyntaxError: Unexpected token o in JSON at position 1 — that lowercase o is the second character of [object Object]. It means you passed a JavaScript object directly to JSON.parse() instead of a JSON string. This article explains every variant of this error and how to fix each one in under a minute.

Why the Error Says "token o"

When you pass a non-string value to JSON.parse(), JavaScript coerces it to a string first. A plain object coerces to "[object Object]". The parser sees [ at position 0 (valid — starts an array), then o at position 1 (invalid — not a valid array element). Hence: Unexpected token o at position 1.

JSON.parse({})
// Same as: JSON.parse("[object Object]")
// SyntaxError: Unexpected token o in JSON at position 1

The Most Common Cause: Not Awaiting response.json()

This is the number-one source of this error in real codebases. A fetch() Response object is not a string — it's a ReadableStream wrapper. Passing it to JSON.parse() coerces it to "[object Response]".

// ❌ Wrong — response is a Response object, not a string
const response = await fetch('/api/user');
const data = JSON.parse(response);
// SyntaxError: Unexpected token o in JSON at position 1

// ✓ Correct — let the browser parse the stream as JSON
const data = await response.json();

Similarly, if you call response.text() without await, you get a Promise object (which coerces to "[object Promise]").

// ❌ Missing await — text is a Promise, not a string
const text = response.text();
const data = JSON.parse(text);
// SyntaxError: Unexpected token o in JSON at position 1

// ✓ Correct
const text = await response.text();
const data = JSON.parse(text);

Other Objects That Trigger This Error

What you passedWhat JS coerces it toError token
{} or any plain object"[object Object]"token o at position 1
undefined"undefined"token u at position 0
true / false"true" / "false" — actually valid JSON!no error
A Response object"[object Response]"token o at position 1
A Promise"[object Promise]"token o at position 1
null"null" — valid JSONno error (returns null)

Cause: Passing an Object Instead of Serialising It

A related mistake: trying to "parse" a JavaScript object you already have in memory. JSON.parse() takes a string; if you already have an object, you don't need to parse it at all.

const config = { host: 'localhost', port: 3000 };

// ❌ Nonsensical — config is already an object
const parsed = JSON.parse(config);

// ✓ If you want a deep clone, use structuredClone or stringify + parse
const clone = JSON.parse(JSON.stringify(config));

// ✓ If you want to send it, use JSON.stringify
const body = JSON.stringify(config);

Cause: Python or Other Back-End Returning Wrong Content-Type

Sometimes the server returns a plain-text error message (like "ok" or "not found") with a 200 status and no Content-Type header. The front end assumes JSON, calls response.json(), and gets an unexpected token error because the body is not valid JSON.

// Server sends the literal string: ok
// Browser tries to parse it as JSON:
JSON.parse("ok")
// SyntaxError: Unexpected token o in JSON at position 0

Fix: Always check response.ok and response.headers.get('Content-Type') before calling response.json():

const response = await fetch('/api/action');
if (!response.ok) {
  const text = await response.text();   // safe — might not be JSON
  throw new Error(`HTTP ${response.status}: ${text}`);
}
const data = await response.json();

Less-Common [object X] Variants

Any value whose toString() returns a string starting with [object triggers the same error at position 1. Beyond [object Object], the ones you actually see in real code:

  • [object Module] — passing a dynamic-import result: const mod = await import('./data.json', { with: { type: 'json' } }); you want mod.default, not mod.
  • [object AsyncFunction] — passing an async function reference instead of calling it: stringify fn() (the promise's resolved value), not fn.
  • [object HTMLDocument] — passing document by mistake (e.g. from a stale debugger snippet).
  • [object FormData] — using fetch(url, { body: formData }) is fine, but JSON.parse(formData) is not — convert with Object.fromEntries(formData) first.

Quick Fix Checklist

  • fetch + JSON.parse? Replace with await response.json().
  • Missing await anywhere in the chain? Every async call before the parse must be awaited.
  • Already have an object? You don't need to parse it — use it directly or clone with structuredClone().
  • Server returning non-JSON on error? Read with response.text(), not response.json(), when the response might not be JSON.

Frequently Asked Questions

What does "Unexpected token o in JSON at position 1" mean?

You passed a JavaScript object to JSON.parse(). JavaScript coerces the object to the string "[object Object]"; the parser accepts [ at position 0 but rejects the o at position 1.

How do I fix it with fetch?

Don't call JSON.parse(response). Use await response.json(), which reads the response stream and parses it for you. Make sure every step in the chain is awaited.

Do I need JSON.parse if I already have an object?

No. JSON.parse() only converts a JSON string into a value. If you already hold an object, use it directly, or deep-clone with structuredClone() instead of JSON.parse(JSON.stringify(obj)).

Is "Unexpected token o" the same as "[object Object] is not valid JSON"?

They share the same root cause — an object stringified before parsing. Newer V8 builds print the [object Object] is not valid JSON wording; older ones print Unexpected token o. See Fix "[object Object] is not valid JSON" for the full breakdown.

Inspect the Raw Response

If you're unsure what your API is returning, paste the raw body into JSON Fix to validate it. If it's not valid JSON, the validator will tell you exactly what's wrong.