Postgres: invalid input syntax for type json — causes and fixes
Why PostgreSQL rejects a value with 'invalid input syntax for type json', how to read the DETAIL line, and the exact fix for single quotes, empty strings, NULs, and double-encoded JSON.
What does "invalid input syntax for type json" mean in Postgres?
PostgreSQL raises this when a value you are inserting into a json or jsonb column is not valid JSON. The DETAIL line tells you exactly where it stopped. The usual causes are single-quoted strings, an empty string instead of NULL, a bare word that should be quoted, a NUL byte (\u0000) inside a string, or JSON that was encoded twice.
Read the DETAIL line first
Postgres always tells you what it expected. The DETAIL is more useful than the headline error:
ERROR: invalid input syntax for type json
DETAIL: Token "'" is invalid.
CONTEXT: JSON data, line 1: {'...
ERROR: invalid input syntax for type json
DETAIL: The input string ended unexpectedly.
CONTEXT: JSON data, line 1: {"a": 1Cause 1 — single quotes instead of double quotes
This is by far the most common. JSON requires double quotes; single quotes are a SQL string delimiter, not JSON syntax.
-- ✗ Fails: the JSON itself uses single quotes
INSERT INTO t (payload) VALUES ('{'name': 'Ada'}');
-- ✓ Correct: single quotes delimit the SQL literal,
-- double quotes are inside the JSON
INSERT INTO t (payload) VALUES ('{"name": "Ada"}');
-- ✓ Or use dollar quoting to avoid escaping entirely
INSERT INTO t (payload) VALUES ($${"name": "O'Brien"}$$);Cause 2 — empty string instead of NULL
An empty string is not valid JSON. If a column is nullable, insert NULL; if you need an empty document, insert '{}' or 'null'.
-- ✗ ERROR: invalid input syntax for type json
INSERT INTO t (payload) VALUES ('');
-- ✓ Pick the one you actually mean
INSERT INTO t (payload) VALUES (NULL); -- no value
INSERT INTO t (payload) VALUES ('{}'); -- empty object
INSERT INTO t (payload) VALUES ('null'); -- JSON null
-- Clean an existing column
UPDATE t SET payload = NULL WHERE payload::text = '';Cause 3 — a NUL byte in the string
PostgreSQL's jsonb cannot store \u0000, even though it is legal in the JSON spec. This bites when importing scraped or binary-ish data.
ERROR: unsupported Unicode escape sequence DETAIL: \u0000 cannot be converted to text. -- Strip NULs before inserting SELECT replace(payload, '\u0000', '')::jsonb;
Cause 4 — double-encoded JSON
If an application JSON-encodes a value that was already a JSON string, Postgres receives a quoted string containing JSON rather than an object. It parses — but as a string, so key lookups return nothing.
-- What arrived (a JSON *string*, not an object):
"{\"name\": \"Ada\"}"
-- Detect it:
SELECT jsonb_typeof(payload) FROM t; -- 'string' instead of 'object'
-- Recover it:
UPDATE t SET payload = (payload #>> '{}')::jsonb
WHERE jsonb_typeof(payload) = 'string';Validate before you insert
Postgres 16+ ships an IS JSON predicate, which lets you find bad rows without aborting the statement:
-- Postgres 16+ SELECT * FROM staging WHERE NOT (raw IS JSON); -- Any version: quarantine bad rows during load INSERT INTO t (payload) SELECT raw::jsonb FROM staging WHERE raw IS NOT NULL AND raw <> '';
Paste your JSON into the validator →
Get the exact line, column, and a fix hint in seconds — no upload, no signup.
Open JSON Validator →json vs jsonb — does it matter here?
json— stores the text almost verbatim; still rejects invalid syntax, but preserves key order and duplicates.jsonb— parses into a binary form: strips whitespace, reorders keys, keeps only the last duplicate key, and rejects\u0000.- Both raise invalid input syntax for type json for malformed text, so the fixes above apply to either.