json_last_error_msg
Returns a human-readable message describing the last JSON encode, decode or validate error.
json_last_error_msg(): string- Added in PHP 5.5.0
Return value
stringThe error message for the last JSON operation, or the string "No error" when the last call succeeded.
Changelog
- PHP 8.3.0The reported state is also set by json_validate(), not just json_encode() and json_decode().
- PHP 7.3.0Calls that pass JSON_THROW_ON_ERROR no longer update this message; the reason is carried by the thrown JsonException instead.
json_last_error_msg is the readable half of PHP's JSON error reporting. Where json_last_error() gives you an integer constant, this returns the matching English sentence — "Syntax error", "Maximum stack depth exceeded", "Malformed UTF-8 characters, possibly incorrectly encoded" — and the literal string "No error" when nothing went wrong.
Use it for output aimed at a person: a log line, a CLI message, a developer-facing API error body. It is far more useful than false or null when you are staring at a 200 KB payload from a third party and need to know what the parser objected to.
Two things to be careful about
First, the state is global and gets overwritten by the next JSON call, including a successful one. Capture the message on the line right after the failing call, before any other code runs.
Second, the exact wording is not an API. It has changed between PHP versions and may change again. Comparing it with === to decide control flow is a bug waiting for the next upgrade — switch on the JSON_ERROR_* constants from json_last_error() and use this string only for display.
Calls that pass JSON_THROW_ON_ERROR never set it; catch the JsonException and read getMessage() instead.
Example
When to use it
- Log why an inbound webhook payload could not be parsed, together with the raw body.
- Show a developer-facing reason in a 400 response instead of a bare "invalid JSON".
- Print a useful message from a CLI import script when a data file fails to decode.
- Debug an encode failure caused by invalid UTF-8 somewhere in a large nested array.
Frequently asked questions
What does "No error" from json_last_error_msg mean?
It means the most recent json_encode, json_decode or json_validate call succeeded. It can also appear misleadingly if another JSON call ran since the failure, or if the failing call used JSON_THROW_ON_ERROR, which does not update this state.
Can I compare json_last_error_msg to a string like "Syntax error"?
You should not. The wording is not a stable API and has changed between PHP versions; compare json_last_error() against constants such as JSON_ERROR_SYNTAX instead and use the message only for display.
How do I get the JSON error message when using JSON_THROW_ON_ERROR?
Catch the JsonException and call getMessage() on it, which returns the same text. getCode() returns the matching JSON_ERROR_* integer.
More examples
A safe decode helper
The helper tests the integer code for control flow and uses the message only for the text it reports. Notice the last line: the valid literal null decodes fine and is reported as ok, which a plain `if ($value === null)` check would have wrongly rejected.
<?php
function try_decode(string $raw): string {
$value = json_decode($raw, true);
return json_last_error() === JSON_ERROR_NONE
? 'ok: ' . var_export($value, true)
: 'failed: ' . json_last_error_msg();
}
echo try_decode('[1,2,3]'), "\n";
echo try_decode('[1,2,'), "\n";
echo try_decode('null'), "\n";The message goes stale immediately
The first read correctly reports "Syntax error". A single unrelated successful json_encode then resets the shared slot, so the second read prints "No error" even though nothing fixed the broken input. This is why the message must be captured on the very next line.
<?php
json_decode('{broken');
echo json_last_error_msg(), "\n";
$unrelated = json_encode(['fine' => 1]);
// The successful encode above reset the shared state.
echo json_last_error_msg(), "\n";Messages when using exceptions instead
JsonException carries the same text in getMessage() and the same JSON_ERROR_* value in getCode(), so you lose nothing by switching to exceptions. The final line prints "No error" because the throwing call never touched the global state.
<?php
try {
json_decode('{"a":1,,}', true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
echo 'exception: ', $e->getMessage(), "\n";
echo 'code: ', $e->getCode(), "\n";
}
echo 'json_last_error_msg: ', json_last_error_msg(), "\n";
Discussion