mastra-ai/mastra · error

Invalid token

Error message

Invalid token

What it means

getTokenIssuer decodes an access token's issuer (iss claim) and throws 'Invalid token' when jwt.decode returned null — meaning the string could not be decoded as a JWT at all (malformed structure, not three dot-separated base64 segments, or undecodable payload). Related sibling throws cover a missing/non-object payload ('Invalid token payload') and a missing iss ('Invalid token header').

Source

Thrown at packages/auth/src/utils.ts:12

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');

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Log/inspect the raw accessToken received — confirm it looks like header.payload.signature
  2. Fix the client/sender to transmit a real JWT (correct token type in the Authorization header)
  3. Check for truncation/corruption in transit (headers split, whitespace, URL encoding issues)
  4. Before calling, guard: const d = await decodeToken(t); if (!d) handle invalid token instead of throwing

Example fix

// before
const issuer = await getTokenIssuer(await decodeToken(token));
// after
const decoded = await decodeToken(token);
if (!decoded) throw new UnauthorizedError('Malformed token');
const issuer = getTokenIssuer(decoded);
Defensive patterns

Strategy: type-guard

Validate before calling

/^eyJ[\w-]+\.[\w-]+\.[\w-]*$/.test(token) // quick 3-part JWT shape check before decodeToken
export function looksLikeJwt(t: string) { return /^eyJ[\w-]+\.[\w-]+\.[\w-]*$/.test(t); }

Type guard

export function isDecodedJwt(d: jwt.JwtPayload | null): d is jwt.JwtPayload {
  return d != null && typeof d === 'object' && typeof d.iss === 'string';
}
const decoded = await decodeToken(token);
if (!isDecodedJwt(decoded)) throw new UnauthorizedError('Malformed or non-JWT credential');

Try / catch

try {
  const issuer = await getTokenIssuer(await decodeToken(token));
} catch (e) {
  if (e instanceof Error && e.message === 'Invalid token') {
    // token did not decode: return 401 'malformed token' instead of a 500
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling getTokenIssuer(decodeToken(accessToken)) where accessToken is not a valid JWT: garbage/truncated string, opaque token (e.g. a random API key), a token that was URL-corrupted, or decoding a token type jsonwebtoken's decode cannot parse.

Common situations: Clients sending the wrong credential type in the Authorization header, copy/paste truncation of tokens, tokens mangled by proxies/logging, or expecting an id_token but receiving an opaque refresh token.

Understand the failure class

Related errors


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