JSON.parse 'Unexpected token o in JSON at position 1' — Cause and Fix

If you see SyntaxError: Unexpected token 'o', "[object Obj"... is not valid JSON, the cause is always the same: you passed a JavaScript object directly to JSON.parse() instead of a JSON string.

Why the error says 'o' at position 1

JSON.parse() requires a string argument. When you pass something that is not a string, JavaScript calls .toString() on it before passing it to the parser. A plain JavaScript object has no custom toString(), so it inherits Object.prototype.toString, which returns the string "[object Object]".

Now JSON.parse("[object Object]") runs. The parser sees [ — a valid JSON array start — then immediately sees o, which is not a valid JSON value. Position 1 is the character right after the opening bracket.

const obj = { name: "Alice" };

// ❌ Wrong — obj.toString() = "[object Object]"
JSON.parse(obj);
// SyntaxError: Unexpected token 'o', "[object Obj"... is not valid JSON

// ✅ Correct — obj is already a JS object, use it directly
console.log(obj.name); // "Alice"

The three ways this error appears

1. Parsing an object that is already parsed

The most common case: you fetch data, response.json() already returns a JavaScript object, and then you mistakenly pass that object to JSON.parse() again.

// ❌ Double parse — response.json() already returns an object
const response = await fetch('/api/data');
const data = await response.json();     // data is now a JS object
const parsed = JSON.parse(data);        // ❌ data is NOT a string

// ✅ Just use data directly
const parsed = data;                    // already a JS object

2. Parsing form input or state without stringifying first

const formData = {
    username: document.getElementById('username').value,
    age: parseInt(document.getElementById('age').value)
};

// ❌ formData is an object, not JSON
JSON.parse(formData);

// ✅ If you need a JSON string, stringify first
const jsonString = JSON.stringify(formData);

// ✅ Or just use formData directly — it's already an object
sendToServer(formData);

3. Passing state from a React/Vue component

// ❌ state is already an object
const [user, setUser] = useState({ name: "Alice" });
const parsed = JSON.parse(user); // SyntaxError

// ✅ If you need to deep-clone an object using JSON round-trip:
const clone = JSON.parse(JSON.stringify(user)); // ✅ stringify first, then parse

How to tell if something is already an object

const value = await response.json();

console.log(typeof value);           // "object" — already parsed, do not call JSON.parse
console.log(typeof value === 'string'); // false

// Only call JSON.parse when the value is a string
if (typeof value === 'string') {
    const parsed = JSON.parse(value);
} else {
    // value is already a JS object/array — use it directly
}

When would you legitimately JSON.parse a string?

The correct input to JSON.parse() is always a string that contains valid JSON:

// ✅ Parsing JSON from localStorage (stored as string)
const stored = localStorage.getItem('user');
const user = JSON.parse(stored);

// ✅ Parsing a JSON string received from a WebSocket
socket.onmessage = (event) => {
    const data = JSON.parse(event.data); // event.data is a string
};

// ✅ Parsing a hardcoded JSON string
const data = JSON.parse('{"name":"Alice","age":30}');

Validate the JSON string before parsing

If you are not sure whether a value is a valid JSON string, paste it into the JSON Validator to check before running JSON.parse() in your code. You can also use the JSON Formatter to inspect the structure.

Frequently Asked Questions

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

It means you passed a JavaScript object to JSON.parse() instead of a JSON string. JavaScript calls toString() on the object, producing "[object Object]". The JSON parser sees the opening [, then the o at position 1 which is not a valid JSON value, and throws.

How do I fix this error?

Check what you are passing to JSON.parse(). If it is already a JavaScript object (e.g., the return value of response.json()), you do not need to parse it again — just use it directly. Only call JSON.parse() on a string value.

How do I deep-clone an object using JSON?

Stringify first, then parse: const clone = JSON.parse(JSON.stringify(original)). This is the only legitimate reason to call both together on the same value.

Why does JavaScript not throw an error when I pass an object to JSON.parse?

JavaScript is dynamically typed and coerces arguments. When JSON.parse receives a non-string, it calls String() on the argument first, producing "[object Object]". The type coercion succeeds — it's the subsequent JSON parsing of that string that throws.