{"record":{"id":"2f70c6d5ae3bedc0","repo":"decolua/9router","slug":"callbackparams-error-description-callbackpara-2f70c6","errorCode":null,"errorMessage":"${callbackParams.error_description || callbackParams.error}","messagePattern":"\\$\\{callbackParams\\.error_description \\|\\| callbackParams\\.error\\}","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/lib/oauth/services/oauth.js","lineNumber":72,"sourceCode":"        await new Promise((resolve, reject) => {\n          const timeout = setTimeout(() => {\n            reject(new Error(\"Authentication timeout (5 minutes)\"));\n          }, OAUTH_TIMEOUT);\n\n          const checkInterval = setInterval(() => {\n            if (callbackParams) {\n              clearInterval(checkInterval);\n              clearTimeout(timeout);\n              resolve();\n            }\n          }, 100);\n        });\n\n        spinner.stop();\n        close();\n\n        if (callbackParams.error) {\n          throw new Error(callbackParams.error_description || callbackParams.error);\n        }\n\n        if (!callbackParams.code) {\n          throw new Error(\"No authorization code received\");\n        }\n\n        return callbackParams;\n      },\n    };\n  }\n\n  /**\n   * Exchange authorization code for tokens\n   */\n  async exchangeCode(code, redirectUri, codeVerifier, contentType = \"application/x-www-form-urlencoded\") {\n    const body =\n      contentType === \"application/json\"\n        ? JSON.stringify({","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/lib/oauth/services/oauth.js#L54-L90","documentation":"During the browser-based OAuth flow, startAuthFlow spins up a local HTTP server and the provider redirects back to it. If the redirect URL contains an `error` query parameter (standard OAuth2 error response: access_denied, invalid_scope, etc.), waitForCallback throws an Error whose message is the `error_description` if present, otherwise the `error` code itself. This surfaces the provider-side refusal of authorization instead of letting the flow continue with no code.","triggerScenarios":"Calling the returned waitForCallback() when the provider redirects to http://localhost:<port>/callback?error=... (optionally with error_description) — i.e. the user or provider denied authorization on the consent screen.","commonSituations":"User clicked 'Cancel'/'Deny' on the provider consent page (access_denied); the app's requested scopes are not approved for the client (invalid_scope); the provider account lacks the required entitlement; redirect_uri/client_id misconfiguration causing the provider to bounce the request with an error parameter.","solutions":["Retry the flow and click 'Allow'/approve on the provider's consent screen.","Read the thrown message: OAuth codes like access_denied or invalid_scope indicate a scope/client configuration problem — fix the requested scopes or client settings.","Verify the app's client_id / redirect_uri registration with the provider matches what startAuthFlow generates.","Ensure the authorizing account has the required provider entitlements (e.g. active subscription)."],"exampleFix":"// before\nconst params = await flow.waitForCallback(); // throws 'access_denied'\n// after\ntry {\n  const params = await flow.waitForCallback();\n} catch (e) {\n  if (e.message.includes(\"access_denied\")) {\n    console.error(\"Authorization was denied — approve the consent prompt and retry\");\n  }\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"// Nothing to check before the call — the error originates from the provider redirect.\n// Pre-flight you CAN do: verify client_id/redirect_uri/scopes are registered with the provider\n// so the provider does not bounce with an error parameter.","typeGuard":"function isOAuthErrorParams(params) {\n  return params != null && typeof params.error === \"string\" && params.error.length > 0;\n}","tryCatchPattern":"try {\n  const params = await flow.waitForCallback();\n  // proceed with params.code\n} catch (e) {\n  if (/access_denied/i.test(e.message)) {\n    console.error(\"User denied authorization — retry and approve the consent prompt\");\n  } else if (/invalid_scope/i.test(e.message)) {\n    console.error(\"Requested scopes not allowed — adjust requested scopes\");\n  } else {\n    throw e;\n  }\n}","preventionTips":["Instruct users to click Allow/Approve on the consent screen when running CLI auth.","Register the exact redirect_uri (http://localhost:<port>/callback) and scopes with the provider.","Ensure the authorizing account has the required subscription/entitlements.","Detect denial codes (access_denied, invalid_scope) in the message and show a targeted retry prompt."],"tags":["oauth","authorization","callback","consent-denied","cli"],"backgroundTag":"oauth-access-denied","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}