JSON troubleshooting
PHP json_decode returns null: Syntax error and other causes
Find out why json_decode returns null with JSON_THROW_ON_ERROR or json_last_error_msg, then fix syntax errors, BOMs, invalid UTF-8 and depth limits.
What the error means
json_decode returns null when decoding fails, and also when the JSON text is literally null. Without flags it does not say why. Pass JSON_THROW_ON_ERROR (PHP 7.3 and later) to get a JsonException, or call json_last_error_msg() right after the call. Syntax error covers most malformed input: a UTF-8 byte-order mark, single quotes, trailing commas, comments, invalid escapes, an empty string or HTML printed before the JSON. Invalid UTF-8 and control characters have their own messages. PHP does not report where the error is.
Exact messages by runtime
Messages depend on the input and runtime version. Node and Python rows were reproduced locally. Other rows cite official documentation or source. Rows marked as fragments or templates preserve the source wording and are not complete exception transcripts. Browsers and libraries may append location details.
| Runtime | Message | Input and evidence |
|---|---|---|
| PHP / json_last_error_msg() | Syntax error | json_decode("{'id': 1}")Official php-src 8.5.11 message; json_last_error() returns 4 (JSON_ERROR_SYNTAX) |
| PHP / JSON_THROW_ON_ERROR | JsonException: Syntax error | json_decode($json, flags: JSON_THROW_ON_ERROR)Official php-src 8.5.11 source: same message, exception code 4 |
| PHP, byte-order mark | Syntax error | "\xEF\xBB\xBF{}", a UTF-8 BOM before the JSONphp-src 8.5.11 scanner: a UTF-8 character outside a string is a syntax error |
| PHP, empty string | Syntax error | json_decode('')php-src 8.5.11 json_decode: empty input sets JSON_ERROR_SYNTAX |
| PHP, invalid UTF-8 | Malformed UTF-8 characters, possibly incorrectly encoded | Windows-1252 bytes such as \xE9 inside a stringOfficial php-src 8.5.11 message (JSON_ERROR_UTF8) |
| PHP, deep nesting | Maximum stack depth exceeded | Nesting deeper than the depth argument, 512 by defaultOfficial php-src 8.5.11 message (JSON_ERROR_DEPTH) |
| PHP, valid null | No error | json_decode('null') returns nullOfficial php-src 8.5.11 message; decoding succeeded |
Causes you can reproduce
The text uses JavaScript or Python syntax
Single quotes, unquoted keys, trailing commas and comments are all reported as Syntax error, without a position.
<?php
var_dump(json_decode("{'id': 1}")); // NULL
echo json_last_error_msg(); // Syntax errorA byte-order mark precedes the JSON
Files saved as "UTF-8 with BOM" start with the bytes EF BB BF. file_get_contents keeps them, and json_decode rejects them.
<?php
$json = "\xEF\xBB\xBF" . '{"id": 1}'; // as read from a file saved with a BOM
var_dump(json_decode($json)); // NULL
echo json_last_error_msg(); // Syntax errorThe text is not UTF-8
JSON text must be UTF-8. An export in Windows-1252 or ISO-8859-1 fails on its first accented character.
<?php
$json = "{\"name\": \"Ren\xE9\"}"; // é encoded as Windows-1252
var_dump(json_decode($json)); // NULL
echo json_last_error_msg(); // Malformed UTF-8 characters, possibly incorrectly encodedHow to fix it
- Decode with JSON_THROW_ON_ERROR, or check json_last_error() immediately after json_decode. The next json_encode or json_decode call replaces the stored error.
- Inspect the first bytes with bin2hex(substr($json, 0, 16)). efbbbf is a byte-order mark, and 3c is <, the start of HTML or a PHP warning.
- Fix syntax errors where the JSON is produced. Strip a byte-order mark only where you read the file, and convert legacy encodings to UTF-8 before decoding.
- With exceptions enabled, a null result means the JSON text was null. Without them, compare json_last_error() with JSON_ERROR_NONE.
Decode with exceptions
The exception message is the same text json_last_error_msg() returns, and its code is the JSON_ERROR_* constant.
<?php
try {
$data = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
error_log('Invalid JSON: ' . $e->getMessage() . ' (code ' . $e->getCode() . ')');
throw $e;
}Remove a UTF-8 byte-order mark from a file
str_starts_with needs PHP 8.0 or later.
<?php
$json = file_get_contents('data.json');
if (str_starts_with($json, "\xEF\xBB\xBF")) {
$json = substr($json, 3);
}
$data = json_decode($json, true, 512, JSON_THROW_ON_ERROR);Convert a legacy encoding first
Name the real source encoding. mb_convert_encoding needs the mbstring extension.
<?php
$json = mb_convert_encoding($legacy, 'UTF-8', 'Windows-1252');
$data = json_decode($json, true, 512, JSON_THROW_ON_ERROR);Find the exact position with Tool Kumar
- Copy the exact input text, including its original whitespace. For HTTP failures, copy the raw response body.
- Open the Tool Kumar JSON validator and paste the text. The document stays in your browser.
- Read the line and column in the error or fixable-issue banner. For an invalid document, select Go to error to move the caret to that location.
- Inspect the preceding token as well as the marked character. A parser marks where it discovered the problem; the missing quote, separator or value can start earlier.
Python character offsets are zero-based, while its displayed lines and columns are one-based. V8 string positions count UTF-16 code units; Go offsets count bytes read. Unicode text can make these offsets differ. Use the original text and its line/column context rather than copying an offset between runtimes.
Tool Kumar can classify some input as fixable loose JSON. Review a proposed change before using the JSON repair tool, then validate the result against your API contract. For empty input, inspect the producer or response because there is no character to select.
Frequently asked questions
Why does json_decode return null for JSON that looks valid?
Common reasons are an invisible UTF-8 byte-order mark, text that is not UTF-8, nesting deeper than the depth limit, or JSON that is literally null. Check the error message and the first bytes.
Does json_last_error_msg show where the error is?
No. PHP reports only the kind of error. Paste the text into a validator to find the line and column.
Should I use JSON_INVALID_UTF8_IGNORE or JSON_INVALID_UTF8_SUBSTITUTE?
They make decoding succeed by dropping invalid bytes or replacing them with U+FFFD, which changes the data. Prefer converting the text from its real encoding.
Why does JSON from a WordPress POST field fail?
WordPress adds slashes to request data, so quotes arrive escaped with backslashes. Remove them with wp_unslash() before calling json_decode.
Related errors and tools
- JSON.parse: unexpected character at line 1 column 1 of the JSON data
- Expecting property name enclosed in double quotes
- JSON trailing comma errors in objects and arrays
- Bad control character in string literal in JSON
- Bad escaped character in JSON / Invalid \escape
- All JSON error guides
- JSON syntax reference
- Generate PHP classes from valid JSON
Standards and diagnostic sources
RFC 8259 and ECMA-404 define JSON syntax. Diagnostic wording belongs to the parser. The table links each cited message to its source.