mastra-ai/mastra · error
Invalid state token format
Error message
Invalid state token format
What it means
Google's state token format is `<base64Payload>.<hmacSignature>`. verifyStateToken splits on '.', and if the split doesn't yield exactly two parts the string structurally cannot be a token this library issued, so it throws before any signature check. This guards against garbage, URL-mangled, or foreign-format state values reaching the HMAC verification path.
Source
Thrown at auth/google/src/auth-provider.ts:189
): Promise<string> {
const payload: StatePayload = {
s: originalState,
r: redirectUri,
e: Date.now() + STATE_TOKEN_EXPIRY_MS,
n: nonce,
};
const payloadB64 = btoa(JSON.stringify(payload));
const signature = await hmacSign(payloadB64, secret);
return `${payloadB64}.${signature}`;
}
async function verifyStateToken(
stateToken: string,
secret: string,
): Promise<{ originalState: string; redirectUri: string; nonce: string }> {
const parts = stateToken.split('.');
if (parts.length !== 2) {
throw new Error('Invalid state token format');
}
const [payloadB64, signature] = parts as [string, string];
const expectedSig = await hmacSign(payloadB64, secret);
if (!timingSafeEqual(signature, expectedSig)) {
throw new Error('Invalid state token signature');
}
let payload: StatePayload;
try {
payload = JSON.parse(atob(payloadB64)) as StatePayload;
} catch {
throw new Error('Invalid state token payload');
}
if (payload.e < Date.now()) {
throw new Error('State token has expired');
}View on GitHub (pinned to 75dd419e61)
Solutions
- Restart the login flow so the callback receives a freshly issued `<payload>.<signature>` state token.
- Log the received state (shape only) before verification and check for mangling — missing dot, extra dots, stripped characters.
- Ensure the callback route reads the raw `state` query parameter without extra decoding/transformation.
- Clear stale cookies or stored state from older versions and ensure the state round-trips through your frontend untouched.
Example fix
// before: verify whatever arrives
const result = await verifyStateToken(req.query.state as string, secret);
// after
const state = req.query.state;
if (typeof state !== 'string' || !/^[A-Za-z0-9+/=]+\.[A-Za-z0-9+/=]+$/.test(state)) {
return res.redirect('/auth/google/login'); // malformed state, re-issue
}
const result = await verifyStateToken(state, secret); Defensive patterns
Strategy: validation
Validate before calling
function hasValidStateShape(state: unknown): state is string {
return typeof state === 'string' && state.split('.').length === 2 && state.length > 0;
}
if (!hasValidStateShape(req.query.state)) return redirectToLogin(); Type guard
function isStateToken(v: unknown): v is string {
return typeof v === 'string' && /^[^.]+\.[^.]+$/.test(v);
} Try / catch
try {
await verifyStateToken(state, secret);
} catch (e) {
if ((e as Error).message === 'Invalid state token format') {
return restartLoginFlow(); // stale or mangled state
}
throw e;
} Prevention
- Read the raw state query param without extra URL decoding.
- Clear stale state from previous flows/library versions before verifying.
- Check state shape (one dot, two non-empty parts) client- and server-side before calling verify.
When it happens
Trigger: The OAuth callback passes a state value whose split('.') length !== 2 — an empty state, a state that was URL-decoded/re-encoded losing the dot or gaining extra dots, a plaintext CSRF value from an older flow, or a JWT (multiple dots) passed instead.
Common situations: Middleware or framework double-decoding query params and mangling the token; frontend truncating the state query parameter; old cookies/stored state from a previous library version with a different format; passing a signed JWT where the library's state token is expected.
Related errors
- Invalid state redirect suffix
- Invalid state token signature
- Google service account token request failed (${response.stat
- auth callback rejected (invalid state/token); 400 HTML failu
- Invalid state token format
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/6261e98ff007af48.
Report an issue: GitHub.