{"record":{"id":"8697495d4e3065fc","repo":"eyaltoledano/claude-task-master","slug":"oauth-failed","errorCode":"OAUTH_FAILED","errorMessage":"data.error_description || data.error || 'Authentication failed'","messagePattern":"data\\.error_description \\|\\| data\\.error \\|\\| 'Authentication failed'","errorType":"exception","errorClass":"AuthenticationError","httpStatus":null,"severity":"error","filePath":"packages/tm-core/src/modules/auth/services/oauth-service.ts","lineNumber":391,"sourceCode":"\t\t\t\t\t\t);\n\n\t\t\t\t\t\tthis.logger.debug('Successfully decrypted authentication tokens');\n\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttoken: tokens.access_token,\n\t\t\t\t\t\t\trefreshToken: tokens.refresh_token,\n\t\t\t\t\t\t\tuserId: tokens.user_id,\n\t\t\t\t\t\t\temail: tokens.email,\n\t\t\t\t\t\t\texpiresAt: tokens.expires_in\n\t\t\t\t\t\t\t\t? new Date(Date.now() + tokens.expires_in * 1000).toISOString()\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\ttokenType: 'standard',\n\t\t\t\t\t\t\tsavedAt: new Date().toISOString()\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\tcase 'failed':\n\t\t\t\t\t\tthrow new AuthenticationError(\n\t\t\t\t\t\t\tdata.error_description || data.error || 'Authentication failed',\n\t\t\t\t\t\t\t'OAUTH_FAILED'\n\t\t\t\t\t\t);\n\n\t\t\t\t\tcase 'expired':\n\t\t\t\t\t\tthrow new AuthenticationError(\n\t\t\t\t\t\t\t'Authentication flow expired',\n\t\t\t\t\t\t\t'AUTH_TIMEOUT'\n\t\t\t\t\t\t);\n\n\t\t\t\t\tcase 'pending':\n\t\t\t\t\tcase 'authenticating':\n\t\t\t\t\t\t// Still waiting, continue polling\n\t\t\t\t\t\tthis.logger.debug(\n\t\t\t\t\t\t\t`Flow status: ${data.status}, continuing to poll`\n\t\t\t\t\t\t);\n\t\t\t\t\t\tbreak;\n","sourceCodeStart":373,"sourceCodeEnd":409,"githubUrl":"https://github.com/eyaltoledano/claude-task-master/blob/c0c98d367c55296bfe69e65680625b6db437af02/packages/tm-core/src/modules/auth/services/oauth-service.ts#L373-L409","documentation":"OAUTH_FAILED is thrown when the polled flow status is 'failed', meaning the OAuth provider itself rejected the authentication (e.g. access_denied, invalid scope). The error message is the provider's error_description or error code forwarded by the backend.","triggerScenarios":"During `credentials()`, the status endpoint returns `{success:true,status:'failed',error/error_description:...}` after the user completes (or aborts) the browser login — e.g. the user denied consent, the provider returned an OAuth error, or SSO config rejected the login.","commonSituations":"User clicks 'Deny'/'Cancel' on the provider consent screen; OAuth app misconfiguration (wrong redirect URI, disabled client); expired or revoked provider client credentials; org policy blocking the account; expired authorization grant server-side.","solutions":["Read the forwarded error_description to identify the provider error (e.g. access_denied = user cancelled) and retry `credentials()`, completing the browser login and approving access.","If access_denied repeats, check org/SSO policies or use an account with access to the application.","Verify the OAuth app configuration on the backend (redirect URIs, enabled client, scopes).","If errors like invalid_client/invalid_scope appear, the backend OAuth app settings changed — contact the service admin or update the CLI."],"exampleFix":"// before: treats all failures the same\nconst creds = await oauthService.credentials();\n// after: distinguish user-cancellation from real failure\ntry {\n  const creds = await oauthService.credentials();\n} catch (e) {\n  if (e instanceof AuthenticationError && e.code === 'OAUTH_FAILED') {\n    if (/access_denied|cancelled/i.test(e.message)) {\n      console.log('Login cancelled; please approve access in the browser.');\n    }\n    throw e;\n  }\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"// confirm the user has access to the org/app before starting the flow\n// e.g. check provider account / SSO entitlement via an unauthenticated endpoint\nconst ent = await fetch(`${baseUrl}/api/auth/cli/config`).then(r => r.ok);","typeGuard":"function isOAuthFailed(e: unknown): e is AuthenticationError {\n  return e instanceof AuthenticationError && e.code === 'OAUTH_FAILED';\n}","tryCatchPattern":"try {\n  const creds = await oauthService.credentials();\n} catch (e) {\n  if (e instanceof AuthenticationError && e.code === 'OAUTH_FAILED') {\n    // surface the provider's error_description to the user\n    console.error(`Login rejected by provider: ${e.message}`);\n    if (/access_denied/i.test(e.message)) console.error('You must approve access in the browser to continue.');\n    throw e;\n  }\n  throw e;\n}","preventionTips":["Instruct users to click 'Allow' on the consent screen, not 'Deny'.","Verify the backend OAuth app (redirect URI, client enabled, scopes) before rollout.","Check org/SSO policies permit the account to authenticate.","Surface e.message directly — it carries the provider's error_description."],"tags":["oauth","authentication","user-cancelled","provider-error"],"backgroundTag":"oauth-access-denied","analyzedSha":"c0c98d367c55296bfe69e65680625b6db437af02","analyzedAt":"2026-08-29T02:56:26.071Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}