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 1The 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 passed | What JS coerces it to | Error 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 JSON | no 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 0Fix: 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 wantmod.default, notmod.[object AsyncFunction]— passing an async function reference instead of calling it: stringifyfn()(the promise's resolved value), notfn.[object HTMLDocument]— passingdocumentby mistake (e.g. from a stale debugger snippet).[object FormData]— usingfetch(url, { body: formData })is fine, butJSON.parse(formData)is not — convert withObject.fromEntries(formData)first.
Quick Fix Checklist
- fetch + JSON.parse? Replace with
await response.json(). - Missing await anywhere in the chain? Every
asynccall 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(), notresponse.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.
- JSON Fix — validate and repair JSON in the browser
- Fix "[object Object] is not valid JSON" — the complete guide to JSON syntax errors
- Unexpected end of JSON input — when the parser runs out of data before the structure is complete
- Handling broken JSON in JavaScript — safe-parse patterns and error recovery