{"record":{"id":"f256eee159048576","repo":"mastra-ai/mastra","slug":"missing-authorization-code","errorCode":null,"errorMessage":"Missing authorization code","messagePattern":"Missing authorization code","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mastracode/sdk/src/auth/providers/anthropic.ts","lineNumber":64,"sourceCode":"    redirect_uri: REDIRECT_URI,\n    scope: SCOPES,\n    code_challenge: challenge,\n    code_challenge_method: 'S256',\n    state: verifier,\n  });\n\n  return { url: `${AUTHORIZE_URL}?${authParams.toString()}`, verifier };\n}\n\n/**\n * Complete an Anthropic login: parse the pasted authorization input\n * (full URL, `code#state`, or query string), validate its state, and exchange\n * it for tokens using the verifier from `startAnthropicLogin()`.\n */\nexport async function completeAnthropicLogin(input: string, verifier: string): Promise<OAuthCredentials> {\n  const { code, state } = parseAuthorizationInput(input);\n  if (!code) {\n    throw new Error('Missing authorization code');\n  }\n  if (!state || state !== verifier) {\n    throw new Error('Invalid authorization state');\n  }\n\n  const tokenResponse = await fetch(TOKEN_URL, {\n    method: 'POST',\n    // Bound the OAuth exchange so an unresponsive upstream cannot pin the\n    // caller (and, in the shipyard server, the containing project lock)\n    // indefinitely. See 2025-07-23 shipyard latency incident.\n    signal: AbortSignal.timeout(15_000),\n    headers: {\n      'Content-Type': 'application/json',\n    },\n    body: JSON.stringify({\n      grant_type: 'authorization_code',\n      client_id: CLIENT_ID,\n      code,","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/sdk/src/auth/providers/anthropic.ts#L46-L82","documentation":"`completeAnthropicLogin` parses the pasted authorization input (full URL, `code#state`, or query string) via `parseAuthorizationInput` before exchanging it for tokens. It throws 'Missing authorization code' when the parsed input contains no authorization code — i.e. the string was empty, whitespace, a URL with no code parameter, or only a state fragment. This is a client-side validation guard so no token request is wasted on an unusable input.","triggerScenarios":"Calling completeAnthropicLogin('', verifier), calling it with a URL that lacks the `code` query param (e.g. an error redirect like ?error=access_denied), or prompting the user who pastes only the `#state` portion / presses Enter without pasting anything.","commonSituations":"User cancels at Anthropic's hosted callback page and copies the URL without a code; clipboard copy only grabbed part of the `code#state` string; the code was already consumed by a prior exchange attempt and stripped; developer passes the raw callback host URL without params in a scripted flow.","solutions":["Re-prompt the user to copy the complete `code#state` string exactly as displayed on Anthropic's callback page and paste it again.","Check that the pasted input actually contains a code segment (before the `#` for paste format, or a `code=` query param for URL format) before calling the API.","Verify the login was not denied — if Anthropic redirected with ?error=..., the user declined and you should restart with startAnthropicLogin().","Log the raw input (redacted) to confirm what parseAuthorizationInput received."],"exampleFix":"// before\nconst input = new URL(callbackRedirectUrl).searchParams.get('s') ?? ''; // wrong field, empty\nawait completeAnthropicLogin(input, verifier); // throws 'Missing authorization code'\n// after\nconst input = new URL(callbackRedirectUrl).searchParams.get('code') ?? '';\nif (!input) throw new Error('OAuth redirect did not contain a code — user likely denied access');\nawait completeAnthropicLogin(input, verifier);","handlingStrategy":"validation","validationCode":"function extractCode(input: string): string | null {\n  const trimmed = input.trim();\n  if (!trimmed) return null;\n  try {\n    const url = new URL(trimmed);\n    return url.searchParams.get('code');\n  } catch {\n    const [code] = trimmed.split('#');\n    return code || null;\n  }\n}\n// before calling: if (!extractCode(input)) re-prompt the user;","typeGuard":null,"tryCatchPattern":"try {\n  await completeAnthropicLogin(input, verifier);\n} catch (e) {\n  if (e instanceof Error && e.message === 'Missing authorization code') {\n    showPrompt('Paste the full code#state string shown on the authorization page');\n    return;\n  }\n  throw e;\n}","preventionTips":["Always validate the pasted input contains a code segment before calling the API.","Prompt users to copy the entire `code#state` string, not just part of it.","Handle OAuth error redirects (?error=...) explicitly instead of feeding them to the token exchange.","For scripted flows, extract the code from the URL's `code` query param rather than passing the raw URL blindly."],"tags":["oauth","anthropic","pkce","input-validation"],"backgroundTag":"missing-oauth-authorization-code","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}