{"record":{"id":"2546e3c97f818f6e","repo":"tymondesigns/jwt-auth","slug":"could-not-decode-token-exception-message","errorCode":null,"errorMessage":"Could not decode token: {exception message}","messagePattern":"Could not decode token: (.+?)","errorType":"exception","errorClass":"Tymon\\JWTAuth\\Exceptions\\TokenInvalidException","httpStatus":null,"severity":"error","filePath":"src/Providers/JWT/Lcobucci.php","lineNumber":113,"sourceCode":"            throw new JWTException('Could not create token: '.$e->getMessage(), $e->getCode(), $e);\n        }\n    }\n\n    /**\n     * Decode a JSON Web Token.\n     *\n     * @param  string  $token\n     * @return array\n     *\n     * @throws \\Tymon\\JWTAuth\\Exceptions\\JWTException\n     */\n    public function decode($token)\n    {\n        try {\n            /** @var \\Lcobucci\\JWT\\Token\\Plain */\n            $token = $this->config->parser()->parse($token);\n        } catch (Exception $e) {\n            throw new TokenInvalidException('Could not decode token: '.$e->getMessage(), $e->getCode(), $e);\n        }\n\n        if (! $this->config->validator()->validate($token, ...$this->config->validationConstraints())) {\n            throw new TokenInvalidException('Token Signature could not be verified.');\n        }\n\n        return Collection::wrap($token->claims()->all())\n            ->map(function ($claim) {\n                if ($claim instanceof DateTimeInterface) {\n                    return $claim->getTimestamp();\n                }\n\n                return is_object($claim) && method_exists($claim, 'getValue')\n                    ? $claim->getValue()\n                    : $claim;\n            })\n            ->toArray();\n    }","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/tymondesigns/jwt-auth/blob/6c70930a92710d97e8e52b182fca2176097f33be/src/Providers/JWT/Lcobucci.php#L95-L131","documentation":"Thrown by the Lcobucci provider's decode() when lcobucci/jwt's parser cannot parse the input string into a Plain token; the parser's message is appended and chained. It means the string had a token-like shape but its segments are not valid base64url-encoded JSON. Note the package's TokenValidator usually rejects shape problems first, so reaching this error means there were 3 dot-separated segments whose content could not be parsed.","triggerScenarios":"Calling JWTAuth::parseToken()->authenticate(), JWTAuth::decode(new Token($token)), or the middleware auth:api when the presented string has three segments but the header or payload is not valid base64url JSON, the signature segment contains non-base64url characters, or arbitrary junk like 'undefined.eyJ9.x' / an empty signature body is sent.","commonSituations":"Token mangled by URL encoding/decoding in transit (base64url - and _ converted), double-encoded base64, tokens copied with truncation or inserted line breaks, tokens produced by another library with a non-standard encoding, or a client concatenating strings into the Authorization header.","solutions":["Log the exact raw token received and compare it byte-for-byte with the token your issuer produced (often a transport/encoding mangling, not a JWT problem)","Manually decode the segments to find the broken one: list($h, $p, $s) = explode('.', $token); json_decode(base64_decode(strtr($h, '-_', '+/'))); - whichever segment fails is the culprit","Fix the client so the token is transmitted unchanged; if it came through a URL, make sure it is not urldecoded twice or re-encoded","If the token is genuinely corrupt, have the client discard it and obtain a fresh one via login/refresh"],"exampleFix":"// before - sending a mangled token\n$response = $client->withHeaders(['Authorization' => 'Bearer ' . urlencode($token)])->get($url);\n\n// after - JWTs are URL-safe already; send unchanged\n$response = $client->withHeaders(['Authorization' => 'Bearer ' . $token])->get($url);","handlingStrategy":"validation","validationCode":"// Cheap pre-check before handing the string to decode()\nfunction isParsableJwt(string $token): bool\n{\n    $parts = explode('.', $token);\n    if (count($parts) !== 3) {\n        return false;\n    }\n    foreach ([0, 1] as $i) {\n        $json = json_decode((string) base64_decode(strtr($parts[$i], '-_', '+/'), true), true);\n        if (!is_array($json)) {\n            return false;\n        }\n    }\n    return true;\n}","typeGuard":"function isParsableJwt(string $token): bool\n{\n    $parts = explode('.', $token);\n    if (count($parts) !== 3) {\n        return false;\n    }\n    foreach ([0, 1] as $i) {\n        $decoded = base64_decode(strtr($parts[$i], '-_', '+/'), true);\n        if ($decoded === false || json_decode($decoded, true) === null) {\n            return false;\n        }\n    }\n    return true;\n}","tryCatchPattern":"use Tymon\\JWTAuth\\Exceptions\\TokenInvalidException;\n\ntry {\n    $user = auth('api')->parseToken()->authenticate();\n} catch (TokenInvalidException $e) {\n    return response()->json(['error' => 'token_invalid'], 401);\n}","preventionTips":["Send tokens exactly as issued - never urlencode a JWT, it is already URL-safe base64","Keep JWTs on a single line in storage, headers, and logs","Pre-validate shape at your API boundary and reject early with 401 before touching the JWT stack"],"tags":["jwt","php","laravel","token-decoding","lcobucci"],"backgroundTag":"jwt-token-parse-failed","analyzedSha":"6c70930a92710d97e8e52b182fca2176097f33be","analyzedAt":"2026-08-21T02:16:37.040Z","schemaVersion":2},"datasetVersion":"2026-08-21T03:17:12.404Z"}