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

  1. Ensure your auth provider includes the `iss` claim in access tokens (enable issuer/audience settings, e.g. Auth0 'Add claims' or API authorization settings)
  2. Decode the token and check `jwt.decode(token, { complete: true }).payload.iss` before calling
  3. If using self-issued tokens, add `iss` to the sign payload
  4. 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

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

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/23773575c9abf0ed. Report an issue: GitHub.