mastra-ai/mastra · error
Invalid authorization state
Error message
Invalid authorization state
What it means
`completeAnthropicLogin` validates the `state` portion of the pasted authorization input against the PKCE verifier returned by `startAnthropicLogin()`. It throws 'Invalid authorization state' when the state is absent or does not exactly equal the verifier persisted from the login start. This is the OAuth CSRF/replay defense: it proves the callback belongs to the login session that initiated it.
Source
Thrown at mastracode/sdk/src/auth/providers/anthropic.ts:67
code_challenge_method: 'S256',
state: verifier,
});
return { url: `${AUTHORIZE_URL}?${authParams.toString()}`, verifier };
}
/**
* Complete an Anthropic login: parse the pasted authorization input
* (full URL, `code#state`, or query string), validate its state, and exchange
* it for tokens using the verifier from `startAnthropicLogin()`.
*/
export async function completeAnthropicLogin(input: string, verifier: string): Promise<OAuthCredentials> {
const { code, state } = parseAuthorizationInput(input);
if (!code) {
throw new Error('Missing authorization code');
}
if (!state || state !== verifier) {
throw new Error('Invalid authorization state');
}
const tokenResponse = await fetch(TOKEN_URL, {
method: 'POST',
// Bound the OAuth exchange so an unresponsive upstream cannot pin the
// caller (and, in the shipyard server, the containing project lock)
// indefinitely. See 2025-07-23 shipyard latency incident.
signal: AbortSignal.timeout(15_000),
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
grant_type: 'authorization_code',
client_id: CLIENT_ID,
code,
state,
redirect_uri: REDIRECT_URI,
code_verifier: verifier,View on GitHub (pinned to 75dd419e61)
Solutions
- Restart the flow: call startAnthropicLogin() to get a fresh URL+verifier and have the user re-authorize — state mismatch is unrecoverable by design.
- Verify you persisted and passed the exact verifier from the same startAnthropicLogin() call that produced the authorization URL (check keying by session/user ID).
- Confirm the user pastes the entire `code#state` string; a missing or mangled `#state` yields an empty state and triggers this error.
- Ensure concurrent logins do not overwrite each other's stored verifier (scope storage per login session).
Example fix
// before
// verifier stored in a single module-level variable, shared across users
await completeAnthropicLogin(input, globalVerifier); // mismatch when two logins race
// after
const session = await sessionStore.get(loginSessionId);
if (!session?.verifier) throw new Error('No pending login session — restart the OAuth flow');
await completeAnthropicLogin(input, session.verifier); Defensive patterns
Strategy: validation
Validate before calling
// ensure a verifier exists for this login session before completing
const session = await sessionStore.get(loginSessionId);
if (!session?.verifier || session.verifier.length < 43) {
throw new Error('No pending login session — restart the OAuth flow');
}
await completeAnthropicLogin(input, session.verifier); Type guard
function hasPendingLogin(s: unknown): s is { verifier: string } {
return typeof s === 'object' && s !== null &&
typeof (s as { verifier?: unknown }).verifier === 'string' &&
(s as { verifier: string }).verifier.length > 0;
} Try / catch
try {
await completeAnthropicLogin(input, verifier);
} catch (e) {
if (e instanceof Error && e.message === 'Invalid authorization state') {
// state/verifier mismatch is unrecoverable: start a fresh login
const { url, verifier: fresh } = await startAnthropicLogin();
showAuthUrl(url);
return completeAnthropicLogin(await promptForCode(), fresh);
}
throw e;
} Prevention
- Persist the verifier keyed by session/user ID, not in a module-level or global variable.
- In split start/complete flows, survive restarts by storing the verifier durably (DB/redis), not in memory.
- Instruct users to paste the complete `code#state` string — truncation drops the state.
- Discard stale in-flight logins when starting a new one to avoid mixed code/state pairs.
When it happens
Trigger: Calling completeAnthropicLogin with a verifier from a different (or restarted) login attempt than the one whose URL the user opened; the user pasting a code#state from an older authorization attempt; passing the code but truncating the `#state` suffix so state parses as empty; persisting the wrong verifier between two HTTP requests in a split start/complete flow.
Common situations: Server restarts between start and complete, losing the original verifier; user has two login tabs open and mixes their code/state pairs; race condition in storage where the verifier is overwritten by a concurrent login; string truncation of the pasted value at `#`.
Related errors
- Missing authorization code
- Token exchange failed: ${error}
- State mismatch
- Invalid state token format
- Invalid or tampered state token
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/c4d5f986a9ed83eb.
Report an issue: GitHub.