decolua/9router · warning · Error
Access denied
Error message
Access denied
What it means
In the GitHub device flow, after the user visits the verification URL, GitHub reports `error: access_denied` if the user explicitly clicked 'Cancel'/'Deny' on the authorization page. pollAccessToken() detects this, fails the spinner with 'Access denied by user.', and throws "Access denied" at github.js:94. The authentication was refused by the human, not by a network or config problem.
Source
Thrown at src/lib/oauth/services/github.js:94
spinner.succeed("GitHub authentication successful!");
return {
access_token: data.access_token,
token_type: data.token_type,
scope: data.scope,
};
} else if (data.error === "authorization_pending") {
// Continue polling
continue;
} else if (data.error === "slow_down") {
// Increase polling interval
interval += 5000;
continue;
} else if (data.error === "expired_token") {
spinner.fail("Device code expired. Please try again.");
throw new Error("Device code expired");
} else if (data.error === "access_denied") {
spinner.fail("Access denied by user.");
throw new Error("Access denied");
} else {
spinner.fail("Failed to get access token.");
throw new Error(data.error_description || data.error);
}
}
}
/**
* Get Copilot token using GitHub access token
*/
async getCopilotToken(accessToken) {
const response = await fetch(`${GITHUB_CONFIG.copilotTokenUrl}`, {
headers: {
Authorization: `Bearer ${accessToken}`, // GitHub API typically uses Bearer
Accept: "application/json",
"X-GitHub-Api-Version": GITHUB_CONFIG.apiVersion,
"User-Agent": GITHUB_CONFIG.userAgent,
},View on GitHub (pinned to 90b52e06ff)
Solutions
- Re-run the authentication flow and click 'Authorize' instead of 'Cancel' on the GitHub page.
- Review the requested scopes (GITHUB_CONFIG.scopes) and confirm you're comfortable granting them before starting.
- Verify you are authorizing the correct OAuth app shown on the GitHub confirmation screen.
Defensive patterns
Strategy: try-catch
Type guard
function isAccessDenied(data) {
return data !== null && typeof data === 'object' && data.error === 'access_denied';
} Try / catch
try {
const auth = await service.authenticate();
} catch (err) {
if (err.message === 'Access denied') {
console.error('You clicked Deny on the GitHub page. Rerun the flow and click Authorize to continue.');
return; // no point retrying automatically — the user must consent
}
throw err;
} Prevention
- Tell the user up front which scopes will be requested so they are ready to click Authorize.
- Don't auto-retry on access_denied — the user must actively consent on the next run.
- Verify the app name shown on the GitHub confirmation screen matches what the user expects.
- For CI/shared machines, pre-authorize once interactively and store the token instead of re-running the device flow.
When it happens
Trigger: The poll loop received `{"error":"access_denied"}` from the token endpoint because the user clicked the cancel/deny button on the GitHub device-authorization page.
Common situations: User was suspicious of the app requesting scopes and denied it; user pressed the wrong button on the confirmation page; a shared machine's user declined authorization for someone else's CLI session.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- `Device code request failed: ${error}`
- Device code expired
- ${data.error_description || data.error}
- GitHub authentication failed: ${error.message}
- `CodeBuddy state request failed: ${await response.text()}`
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/49cc39abaffa90a6.
Report an issue: GitHub.