{"record":{"id":"cbd4f742819cf5d7","repo":"eyaltoledano/claude-task-master","slug":"code-exchange-failed","errorCode":"CODE_EXCHANGE_FAILED","errorMessage":"Failed to exchange code: ${error.message}","messagePattern":"Failed to exchange code: (.+?)","errorType":"error_code","errorClass":"AuthenticationError","httpStatus":null,"severity":"error","filePath":"packages/tm-core/src/modules/integration/clients/supabase-client.ts","lineNumber":191,"sourceCode":"\n\t\t\tthrow new AuthenticationError(\n\t\t\t\t`Failed to start PKCE flow: ${(error as Error).message}`,\n\t\t\t\t'PKCE_FAILED'\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Exchange authorization code for session (PKCE flow)\n\t */\n\tasync exchangeCodeForSession(code: string): Promise<Session> {\n\t\tconst client = this.getClient();\n\n\t\ttry {\n\t\t\tconst { data, error } = await client.auth.exchangeCodeForSession(code);\n\n\t\t\tif (error) {\n\t\t\t\tthrow new AuthenticationError(\n\t\t\t\t\t`Failed to exchange code: ${error.message}`,\n\t\t\t\t\t'CODE_EXCHANGE_FAILED'\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (!data?.session) {\n\t\t\t\tthrow new AuthenticationError(\n\t\t\t\t\t'No session returned from code exchange',\n\t\t\t\t\t'INVALID_RESPONSE'\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tthis.logger.info('Successfully exchanged code for session');\n\t\t\treturn data.session;\n\t\t} catch (error) {\n\t\t\tif (error instanceof AuthenticationError) {\n\t\t\t\tthrow error;\n\t\t\t}","sourceCodeStart":173,"sourceCodeEnd":209,"githubUrl":"https://github.com/eyaltoledano/claude-task-master/blob/c0c98d367c55296bfe69e65680625b6db437af02/packages/tm-core/src/modules/integration/clients/supabase-client.ts#L173-L209","documentation":"exchangeCodeForSession throws AuthenticationError with code CODE_EXCHANGE_FAILED when the Supabase auth client's exchangeCodeForSession(call) returns an error while converting the OAuth authorization code into a session. The Supabase error message is embedded.","triggerScenarios":"Calling exchangeCodeForSession(code) with a code that Supabase rejects: expired code, already-used code (PKCE codes are single-use), code from a mismatched code_verifier, or a malformed callback code.","commonSituations":"User refreshing the callback page (replays a consumed code), callback URL truncation cutting the code, clock skew invalidating the code, or rerunning the auth flow and exchanging a stale code.","solutions":["Restart the full signInWithPKCE flow to get a fresh code instead of reusing the old one","Avoid re-running exchange on page refresh; mark the code as consumed after first exchange","Verify the callback URL is captured fully and the code_verifier matches the one used at initiation","Check clock synchronization if using the machine in a constrained environment"],"exampleFix":"// before\nawait client.exchangeCodeForSession(savedCode); // stale code\n// after\nconst { url } = await client.signInWithPKCE(); // fresh flow\n// exchange only the new code from this callback","handlingStrategy":"retry","validationCode":"if (!code || code.length < 10) {\n  // reject obviously malformed callback codes before exchanging\n}","typeGuard":"function hasUsableCode(cb: { code?: string | null }): cb is { code: string } {\n  return typeof cb.code === 'string' && cb.code.length > 0;\n}","tryCatchPattern":"try {\n  await client.exchangeCodeForSession(code);\n} catch (e) {\n  if (e instanceof AuthenticationError && e.code === 'CODE_EXCHANGE_FAILED') {\n    await client.signInWithPKCE(); // restart flow: codes are single-use\n  }\n}","preventionTips":["Never re-exchange a consumed code (guard against callback page refreshes)","Mark codes as consumed immediately after a successful exchange","Restart the full PKCE flow rather than retrying exchange with the same code"],"tags":["auth","pkce","oauth","supabase"],"backgroundTag":"oauth-code-exchange-failed","analyzedSha":"c0c98d367c55296bfe69e65680625b6db437af02","analyzedAt":"2026-08-29T02:56:26.071Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}