mastra-ai/mastra · error
Invalid token header
Error message
Invalid token header
What it means
getTokenIssuer decodes a JWT and returns its `iss` (issuer) claim. This error is thrown when the payload decodes to an object but has no truthy `iss` claim. Despite the message saying 'header', the check is `!decoded.payload.iss` — the token lacks the issuer claim the library relies on to route issuer-specific verification.
Source
Thrown at packages/auth/src/utils.ts:14
import jwt from 'jsonwebtoken';
import jwksClient from 'jwks-rsa';
export type JwtPayload = jwt.JwtPayload;
export async function decodeToken(accessToken: string) {
const decoded = jwt.decode(accessToken, { complete: true });
return decoded;
}
export function getTokenIssuer(decoded: jwt.JwtPayload | null) {
if (!decoded) throw new Error('Invalid token');
if (!decoded.payload || typeof decoded.payload !== 'object') throw new Error('Invalid token payload');
if (!decoded.payload.iss) throw new Error('Invalid token header');
return decoded.payload.iss;
}
export async function verifyHmac(accessToken: string, secret: string) {
const decoded = jwt.decode(accessToken, { complete: true });
if (!decoded) throw new Error('Invalid token');
return jwt.verify(accessToken, secret) as jwt.JwtPayload;
}
export async function verifyJwks(accessToken: string, jwksUri: string) {
const decoded = jwt.decode(accessToken, { complete: true });
if (!decoded) throw new Error('Invalid token');
const client = jwksClient({ jwksUri });
const key = await client.getSigningKey(decoded.header.kid);View on GitHub (pinned to 75dd419e61)
Solutions
- Ensure your auth provider includes the `iss` claim in access tokens (enable issuer/audience settings, e.g. Auth0 'Add claims' or API authorization settings)
- Decode the token and check `jwt.decode(token, { complete: true }).payload.iss` before calling
- If using self-issued tokens, add `iss` to the sign payload
- Verify you are passing the access token, not an opaque ID token variant without issuer
Example fix
// before
jwt.sign({ sub: 'user-1' }, secret); // no iss claim
// after
jwt.sign({ sub: 'user-1', iss: 'https://your-issuer.example.com' }, secret); Defensive patterns
Strategy: validation
Validate before calling
function hasIssuerClaim(token: string): boolean {
try {
const payload = JSON.parse(Buffer.from(token.split('.')[1] ?? '', 'base64url').toString('utf8'));
return typeof payload === 'object' && payload !== null && typeof (payload as any).iss === 'string' && (payload as any).iss.length > 0;
} catch {
return false;
}
} Type guard
function hasIss(decoded: jwt.JwtPayload | null): decoded is jwt.JwtPayload & { iss: string } {
return decoded !== null && typeof decoded.iss === 'string' && decoded.iss.length > 0;
} Try / catch
try {
const issuer = getTokenIssuer(token);
} catch (e) {
if (e instanceof Error && e.message === 'Invalid token header') {
// token has no iss claim: token not usable for issuer-based verification; re-issue with iss
}
throw e;
} Prevention
- Enable the issuer claim in your auth provider's token settings (OIDC-compliant tokens include iss)
- When minting your own tokens, always include iss matching your configured issuer URL
- Unit-test that real tokens from your provider contain iss
When it happens
Trigger: Calling getTokenIssuer(accessToken) on a validly decoded JWT whose payload is an object but has no `iss` property (or iss is empty/null), so `decoded.payload.iss` is falsy.
Common situations: Tokens issued by providers that omit `iss` (opaque or first-party tokens), custom-signed internal tokens missing the issuer claim, or testing with self-minted tokens that don't follow OIDC conventions.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Failed to extract ChatGPT account id from OpenAI Codex token
- JWT auth secret is required
- Invalid token
- Invalid token payload
- No token verification method configured
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/23773575c9abf0ed.
Report an issue: GitHub.