← All articles

Fix "[object Object] is Not Valid JSON": Causes and Safer Syntax Fixes

Fix "[object Object] is not valid JSON" by finding where a JavaScript object was coerced to a string, then handle the related JSON syntax errors safely.

"[object Object]" is not valid JSON means a JavaScript object was turned into the literal text [object Object] before somebody tried to parse it, store it, or send it as JSON.

That is different from a trailing comma or a missing quote. With a trailing comma, the original data is still visible and a repair parser can usually fix the syntax. With [object Object], the original object has already been flattened into a useless string. The fix is to find the coercion point and serialize the object correctly with JSON.stringify().

Here is the fastest way to debug it: log the value and its type immediately before the failing parse or request.

console.log(typeof value, value);

If you see object in the first column, do not call JSON.parse(value). You already have a JavaScript value. If you see string [object Object], look earlier in the code for concatenation, a template literal, localStorage.setItem(), URLSearchParams, or a request body that received a raw object.

Quick Diagnosis

Symptom What probably happened First place to check
"[object Object]" is not valid JSON A plain object became the string [object Object] String concatenation, template literals, storage, request body
Unexpected token o in JSON at position 1 Same root cause in older error wording JSON.parse({}) or JSON.parse(response)
Unexpected token < in JSON at position 0 You parsed HTML, usually an error page response.ok, Content-Type, raw response body
Unexpected token u in JSON at position 0 You parsed undefined Missing state, missing prop, missing return, unset variable
Expected a JSON object, array or literal The input is not one of JSON's six value types Empty string, BOM, HTML, undefined, non-JSON text
Trailing comma before '}' JavaScript-style comma in strict JSON Object or array copied from JS code

The table matters because these errors can look related in search results, but the fix is not always the same. [object Object] is a serialization bug. A trailing comma is a syntax bug. HTML is usually a server or routing bug.

What "[object Object]" Actually Means

Every ordinary JavaScript object inherits a default toString() method. When JavaScript is forced to use an object where a string is expected, that method returns [object Object].

const user = { id: 42, name: 'Ada' };

String(user);        // "[object Object]"
`${user}`;           // "[object Object]"
'payload=' + user;   // "payload=[object Object]"

That string is not JSON. If you pass it to JSON.parse(), the parser sees the first character, [, and assumes a JSON array is starting. The next character is o, from the word object, but a JSON array cannot contain a bare word there.

JSON.parse('[object Object]');
// SyntaxError: Unexpected token o in JSON at position 1

Newer runtimes may print "[object Object]" is not valid JSON; older V8 messages often say Unexpected token o in JSON at position 1. They are the same family of mistake.

The Places This Bug Usually Enters

1. Parsing an object you already have

JSON.parse() accepts JSON text. It is not a "make this into an object" function for values that are already objects.

const settings = { theme: 'dark', compact: true };

// Wrong: settings is already an object.
const parsedFromObject = JSON.parse(settings);

// Right: use the value directly.
const parsed = settings;

// If you need a deep copy in modern JavaScript:
const copy = structuredClone(settings);

Use JSON.parse() only when the input type is a string that contains JSON text.

2. Sending a fetch body without JSON.stringify()

When an API expects JSON, the request body must be a JSON string. Passing a plain object as body does not magically make it JSON. In browsers it is commonly converted to [object Object]; in some server runtimes it may be rejected. Either way, it is not the payload your API expects.

const payload = { email: 'ada@example.com', plan: 'pro' };

// Wrong: raw object body.
await fetch('/api/account', {
  method: 'POST',
  body: payload,
});

// Right: serialize and label it.
await fetch('/api/account', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(payload),
});

On the server, read the raw body when debugging. If the body literally says [object Object], the data was already lost before it reached your handler.

3. Calling JSON.parse(response) instead of response.json()

A fetch() response is a Response object, not the JSON body. Passing the response object to JSON.parse() coerces the object instead of reading the stream.

const response = await fetch('/api/user');

// Wrong: response is not text.
const parsedResponse = JSON.parse(response);

// Right: let the browser read and parse the response body.
const data = await response.json();

If the endpoint may return HTML or plain text on errors, guard it first:

const response = await fetch('/api/user');
const contentType = response.headers.get('content-type') || '';

if (!response.ok) {
  const text = await response.text();
  throw new Error(`HTTP ${response.status}: ${text.slice(0, 200)}`);
}

if (!contentType.includes('application/json')) {
  const text = await response.text();
  throw new TypeError(`Expected JSON, got ${contentType}: ${text.slice(0, 80)}`);
}

const data = await response.json();

This one check separates [object Object] bugs from the very common Unexpected token < problem, where the "JSON" response is actually an HTML error page.

4. Building JSON with string concatenation

Manual JSON strings are brittle. The moment one inserted value is an object, a quote, a newline, or a backslash, the string can stop being valid JSON.

const profile = { name: 'Ada', role: 'admin' };

// Wrong: profile becomes [object Object].
const brokenBody = '{"profile": ' + profile + '}';

// Right: build a JavaScript value, then stringify the whole thing.
const body = JSON.stringify({ profile });

This pattern also prevents quote escaping bugs:

const message = 'She said "ship it"';

// Right: JSON.stringify escapes the nested quotes correctly.
const body = JSON.stringify({ message });

Do not try to fix this with replace() calls. Let the serializer handle JSON escaping.

5. Template literals with embedded objects

Template literals have the same coercion behavior as concatenation.

const config = { retries: 3, timeoutMs: 5000 };

// Wrong.
const brokenText = `{"config": ${config}}`;
// {"config": [object Object]}

// Right.
const text = JSON.stringify({ config });

If you need to interpolate one field into a larger object, stringify that field:

const text = `{"config": ${JSON.stringify(config)}, "source": "ui"}`;

But in most cases, stringifying the final object is simpler and safer.

6. localStorage or sessionStorage without stringify

Web storage stores strings. If you pass an object directly, the browser stores [object Object].

const draft = { title: 'Incident notes', tags: ['api', 'json'] };

// Wrong: stores "[object Object]".
localStorage.setItem('draft', draft);

// Later:
JSON.parse(localStorage.getItem('draft'));
// SyntaxError

// Right.
localStorage.setItem('draft', JSON.stringify(draft));

const saved = localStorage.getItem('draft');
const parsed = saved ? JSON.parse(saved) : null;

This is a sneaky version because the crash happens later, often on page reload, while the bad write happened in a different part of the app.

7. URLSearchParams and query strings

Query strings are strings too. Passing a nested object creates a useless value.

const filters = { status: 'open', owner: 'me' };

String(new URLSearchParams({ filters }));
// "filters=%5Bobject+Object%5D"

Choose a format intentionally:

// Option 1: flatten the fields.
const flatQuery = new URLSearchParams({
  status: filters.status,
  owner: filters.owner,
});

// Option 2: send JSON as one encoded parameter.
const jsonQuery = new URLSearchParams({
  filters: JSON.stringify(filters),
});

The first option is easier for humans and caches. The second is useful when the filter shape is genuinely nested.

8. FormData treated like JSON

FormData is valid as a multipart request body, but it is not JSON text.

const formData = new FormData(form);

// Wrong.
JSON.parse(formData);

// If you need a plain object:
const data = Object.fromEntries(formData.entries());

// If the API expects JSON:
await fetch('/api/profile', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(data),
});

If the API expects file uploads, do not stringify FormData; send it as FormData and let the browser set the multipart boundary.

The Fix Pattern I Use

When a code path may receive either a JSON string or an object, normalize it explicitly. Do not rely on JavaScript's implicit coercion.

function ensureJsonText(value) {
  if (typeof value === 'string') {
    JSON.parse(value); // validate that the string is already JSON
    return value;
  }

  if (value === undefined) {
    throw new TypeError('Cannot serialize undefined as a JSON document');
  }

  return JSON.stringify(value);
}

Use it at the boundary where text is required:

await fetch('/api/save', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: ensureJsonText(payload),
});

Inside your application, prefer objects. At the network, storage, file, or clipboard boundary, serialize once.

"[object Object]" vs Double-Encoded JSON

Double encoding is a different bug.

const inner = JSON.stringify({ id: 1 });
const outer = JSON.stringify({ data: inner });

console.log(outer);
// {"data":"{\"id\":1}"}

This is valid JSON, but data is a string containing JSON, not an object. After parsing outer, you would need to parse result.data separately:

const result = JSON.parse(outer);
const data = JSON.parse(result.data);

That may be intentional for logs or message queues, but it is often accidental. If you control both sides, send the nested object directly:

const outer = JSON.stringify({ data: { id: 1 } });

[object Object] means the object was not serialized as JSON at all. Double encoding means it was serialized too early.

"Expected a JSON Object, Array or Literal"

Firefox's JSON viewer and some validators use this wording when the top-level input is not a valid JSON value. JSON has exactly six value types: object, array, string, number, boolean, and null.

Common causes:

  • Empty input: JSON.parse('')
  • Undefined input: JSON.parse(undefined)
  • HTML response: <!doctype html>...
  • Plain text response: ok
  • A byte-order mark or other unexpected character before the JSON

Use a guard that preserves the raw text for debugging:

function parseJsonResponseText(text) {
  if (typeof text !== 'string') {
    throw new TypeError(`Expected a string, got ${typeof text}`);
  }

  const trimmed = text.trimStart();
  if (!trimmed) {
    throw new SyntaxError('Empty response body is not JSON');
  }

  return JSON.parse(trimmed);
}

Do not silently convert every bad response to {} or null. That hides server bugs and makes downstream errors harder to trace.

Common JSON Syntax Errors and Safer Fixes

Error pattern Example Safer fix
Object coerced to string [object Object] Go back to the object and call JSON.stringify()
Empty input '' Guard before parsing; decide whether empty means null, [], or an error
HTML response <!doctype html> Check status, route, auth redirects, and Content-Type
Trailing comma { "a": 1, } Remove structural trailing commas or use a repair parser
Single quotes { 'name': 'Ada' } Use double quotes or regenerate with JSON.stringify()
Unquoted keys { name: "Ada" } Quote keys or regenerate from a JavaScript object
Python literals { "ok": True, "x": None } Convert to true, false, and null
Comments { "debug": true // temp } Remove comments or use JSONC for config files
Raw newline in a string A literal line break inside quotes Encode as \n or build the string with JSON.stringify()
Bad escape "C:\new\file.json" Escape backslashes as C:\\new\\file.json

For syntax-only mistakes such as trailing commas, single quotes, comments, and unquoted keys, a grammar-aware repair tool can help. For [object Object], repair is usually impossible after the string is created because the original keys and values are gone.

When a JSON Corrector Helps

A JSON corrector is useful when the broken text still contains the data:

{
  name: 'Ada',
  active: True,
  roles: ['admin', 'editor',],
}

A repair parser can turn that into strict JSON because the object shape is still present.

{
  "name": "Ada",
  "active": true,
  "roles": [
    "admin",
    "editor"
  ]
}

But this cannot be reconstructed:

[object Object]

That string contains no name, no roles, no nested structure, and no values. The right fix is upstream: find where the object was coerced and serialize there.

Production Checklist

Use this checklist before shipping a JSON fix:

  1. Log typeof value before parsing or sending.
  2. Parse only strings; use objects directly.
  3. Serialize once at the boundary with JSON.stringify().
  4. Add Content-Type: application/json when sending JSON.
  5. Check response.ok before parsing API responses.
  6. Check Content-Type before calling response.json() if the endpoint may return HTML.
  7. Preserve raw response text in error logs, but redact secrets.
  8. Validate business shape with JSON Schema or app-level checks after syntax parsing.

That sequence catches the bug where it starts instead of cleaning up the error message after the useful information is gone.

Frequently Asked Questions

What does "[object Object] is not valid JSON" mean?

A JavaScript object was coerced to the string [object Object], then that string was parsed or sent as JSON. The original object shape is no longer present in the text, so the fix is to serialize the object earlier with JSON.stringify().

How do I fix the [object Object] error in a fetch request?

Use body: JSON.stringify(payload) and set Content-Type: application/json when the API expects JSON. For responses, do not call JSON.parse(response); call await response.json() after checking the response status.

Why does JSON.parse({}) produce "Unexpected token o"?

JSON.parse() first converts non-string inputs to strings. A plain object becomes [object Object]; the parser accepts [ as the start of an array, then rejects the o in object at position 1.

Can a JSON repair tool fix [object Object]?

Usually no. A repair tool can fix malformed JSON when the original keys and values are still visible, such as trailing commas or single quotes. The string [object Object] has already lost the actual data, so you need to fix the code that created it.

What are the most common JSON syntax errors near this one?

The closest neighbors are Unexpected token o from object coercion, Unexpected token < from HTML responses, Unexpected token u from undefined, trailing commas, single quotes, unquoted keys, comments, Python literals, bad escapes, and raw control characters inside strings.

Fix JSON Syntax Online

If the broken text still contains the data, paste it into JSON Fix and run Repair & Format. The tool can clean up common syntax mistakes locally in your browser: trailing commas, single quotes, unquoted keys, comments, Python literals, markdown fences, and truncated structures.

If the text is only [object Object], go back to the source code first. The online fixer can identify the symptom, but it cannot recover properties that were already collapsed by .toString().

Related guides:

Sources

Last reviewed July 2026.