phalcon/cphalcon · error · \JsonException

json_last_error_msg()

Error message

json_last_error_msg()

What it means

Phalcon's JSON decode helper (Phalcon\Support\Helper\Json\Decode, via DecodeTrait::toDecode) wraps json_decode and converts any parse failure into a native \JsonException whose message comes from json_last_error_msg() — e.g. 'Syntax error', 'Maximum stack depth exceeded', 'Malformed UTF-8' (phalcon/Traits/Support/Helper/Json/DecodeTrait.zep:57). This guarantees JSON_THROW_ON_ERROR-like behavior even when the caller did not pass that flag.

Source

Thrown at phalcon/Traits/Support/Helper/Json/DecodeTrait.zep:57

        var decoded, error, message;

        /**
         * Need to clear the json_last_error() before the code below
         */
        let decoded = json_encode(null),
            decoded = json_decode(data, associative, depth, options),
            error   = json_last_error(),
            message = json_last_error_msg();

        /**
         * When JSON_THROW_ON_ERROR is set, json_decode() has already raised a
         * native \JsonException above; otherwise the error is surfaced the
         * same way here.
         */
        if (JSON_ERROR_NONE !== error) {
            json_encode(null);

            throw new \JsonException(message, error);
        }

        return decoded;
    }
}

View on GitHub (pinned to b7419de9cd)

Solutions

  1. Catch \JsonException wherever the input is untrusted and fall back to a safe default.
  2. On PHP 8.3+, pre-validate with json_validate($json, $depth) before decoding.
  3. Fix the producer: encode with json_encode, send correct Content-Length, use JSON_INVALID_UTF8_SUBSTITUTE on the encode side.

Example fix

// before
$data = (new Decode())->__invoke($cookieValue); // throws JsonException on bad JSON

// after
try {
    $data = (new Decode())->__invoke($cookieValue);
} catch (\JsonException $e) {
    $data = [];
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (PHP_VERSION_ID >= 80300 && !json_validate($json)) {
    $json = '{}'; // reject early with a known-good default
}
$data = (new Decode())->__invoke($json, true);

Try / catch

try {
    $data = (new Decode())->__invoke($payload, true);
} catch (\JsonException $e) {
    $logger->warning('Invalid JSON: ' . $e->getMessage());
    $data = []; // safe default for untrusted input
}

Prevention

When it happens

Trigger: Decode::__invoke($json) with truncated JSON (cookie cut off, partial HTTP body), single-quoted strings or unquoted keys, BOM/invalid UTF-8 bytes, or nesting deeper than the default 512 depth. Note UserRemember and similar consumers call this internally but catch InvalidArgumentException, so raw uses of Decode are where this surfaces.

Common situations: Reading remember-me/session cookies that were truncated or tampered with; API responses cut by proxies or timeouts; payloads stored with binary or legacy-encoded (latin1) data; hand-built JSON strings instead of json_encode output.

Related errors


AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21). Data as JSON: /api/errors/712fbfcb9c1b57ba. Report an issue: GitHub.