lcobucci/jwt · error · Lcobucci\JWT\Encoding\CannotDecodeContent
Error while decoding from JSON
Error message
Error while decoding from JSON
What it means
JoseEncoder::jsonDecode wraps json_decode() with JSON_THROW_ON_ERROR; when PHP's json_decode() raises a JsonException (malformed JSON, invalid UTF-8, depth overflow), the encoder rethrows it wrapped in CannotDecodeContent::jsonIssues with the generic message 'Error while decoding from JSON'. It is thrown whenever the library must decode a JSON payload (e.g. a JWT's header or claims segment) and the input is not valid JSON.
Solutions
- Inspect the raw string being passed to jsonDecode() (var_dump/log it) and validate it with json_decode($json) manually to see the exact JsonException message.
- Fix the producer of the JSON: ensure the client sends valid JSON (Content-Type: application/json, no HTML error pages in the body).
- For JWT workflows, verify the token is three dot-separated base64url segments and base64url-decode each segment before JSON decoding.
- Catch CannotDecodeContent at the boundary and return a 400-style response instead of leaking the exception.
- Check that the data wasn't mangled by transport (trailing whitespace is fine, but NUL bytes or charset conversion damage is not).
Example fix
// before
$claims = $encoder->jsonDecode($request->getBody()); // throws CannotDecodeContent on bad JSON
// after
$raw = $request->getBody();
if (json_validate($raw)) {
$claims = $encoder->jsonDecode($raw);
} else {
throw new BadRequestException('Request body is not valid JSON');
} Defensive patterns
Strategy: try-catch
Validate before calling
// PHP 8.3+
if (!function_exists('json_validate') ? json_decode($raw) === null && json_last_error() !== JSON_ERROR_NONE : !json_validate($raw)) {
throw new InvalidArgumentException('Input is not valid JSON');
} Type guard
function isJsonString(?string $raw): bool
{
if ($raw === null || $raw === '') {
return false;
}
json_decode($raw);
return json_last_error() === JSON_ERROR_NONE;
} Try / catch
try {
$data = $encoder->jsonDecode($raw);
} catch (CannotDecodeContent $e) {
// $e->getPrevious() is the JsonException with the exact offset/reason
error_log('JSON decode failed: ' . $e->getPrevious()?->getMessage());
throw new BadRequestException('Malformed JSON input', previous: $e);
} Prevention
- Validate the string with json_validate()/json_decode before decoding it downstream.
- For JWTs, base64url-decode each dot-separated segment before JSON decoding and check the token has exactly 3 segments.
- Log the previous JsonException message (it includes the exact JSON syntax error and offset).
- Ensure HTTP clients send Content-Type: application/json and that proxies don't inject HTML error pages into bodies.
- Watch for invalid UTF-8: run mb_check_encoding($raw, 'UTF-8') on data from external sources.
When it happens
Trigger: Calling jsonDecode() (directly or via token parsing) with a string that is not valid JSON: truncated JWT segments, base64url strings decoded to garbage, double-encoded JSON, or invalid UTF-8 bytes.
Common situations: Receiving a token that was truncated or corrupted in transport/cookies; decoding a payload segment without first base64url-decoding it; a client sending raw form-encoded or XML bodies to an endpoint that expects JSON; PHP json_decode failing on invalid UTF-8 from external systems.
Related errors
- Error while encoding to JSON
- Error while decoding from Base64Url, invalid base64…
- Builder#withClaim() is meant to be used for non-registered…
- The JWT string is missing the Header part
- The JWT string is missing the Claim part
AI-assisted analysis of lcobucci/jwt@375813049c (2026-09-14).
Data as JSON: /api/errors/6617c20bb225d923.
Report an issue: GitHub.
Appendix: source
Thrown at src/Encoding/JoseEncoder.php:37
* A utilitarian class that encodes and decodes data according to JOSE specifications
*/
final readonly class JoseEncoder implements Encoder, Decoder
{
public function jsonEncode(mixed $data): string
{
try {
return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
} catch (JsonException $exception) {
throw CannotEncodeContent::jsonIssues($exception);
}
}
public function jsonDecode(string $json): mixed
{
try {
return json_decode(json: $json, associative: true, flags: JSON_THROW_ON_ERROR);
} catch (JsonException $exception) {
throw CannotDecodeContent::jsonIssues($exception);
}
}
public function base64UrlEncode(string $data): string
{
return SodiumBase64Polyfill::bin2base64(
$data,
SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING,
);
}
public function base64UrlDecode(string $data): string
{
return SodiumBase64Polyfill::base642bin(
$data,
SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING,
);
}View on GitHub (pinned to 375813049c)