tinyhumansai/openhuman · warning · Error
OpenRouter OAuth did not return an authorization code.
Error message
OpenRouter OAuth did not return an authorization code.
What it means
The callback URL parsed and state matched, but there is no `code` query parameter — the authorization server redirected back without granting. Almost always the user denied consent, or the server appended error=access_denied/invalid_request instead of a code.
Source
Thrown at app/src/utils/openrouterOAuth.ts:60
return base64UrlEncode(new Uint8Array(digest));
}
function extractOAuthCode(callbackUrl: string, expectedState: string): string {
let parsed: URL;
try {
parsed = new URL(callbackUrl);
} catch {
throw new Error('OpenRouter OAuth returned an invalid callback URL.');
}
const actualState = parsed.searchParams.get('state');
if (actualState !== expectedState) {
throw new Error('OpenRouter OAuth callback state did not match the request.');
}
const code = parsed.searchParams.get('code');
if (!code) {
throw new Error('OpenRouter OAuth did not return an authorization code.');
}
return code;
}
async function exchangeCodeForKey(
code: string,
verifier: string,
fetchImpl: typeof fetch
): Promise<string> {
const response = await fetchImpl(OPENROUTER_TOKEN_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code, code_verifier: verifier, code_challenge_method: PKCE_METHOD }),
});
let body: OpenRouterExchangeResponse | null = null;
try {
body = (await response.json()) as OpenRouterExchangeResponse;View on GitHub (pinned to a221052e0d)
Solutions
- Read parsed.searchParams.get('error') and surface it — it explains the missing code
- Treat access_denied as user cancellation (info-level UX), not an exception dialog
- If error is invalid_request, fix client_id/scope configuration before retrying
Example fix
// before
const code = parsed.searchParams.get('code');
if (!code) throw new Error('OpenRouter OAuth did not return an authorization code.');
// after — name the actual reason
const oauthErr = parsed.searchParams.get('error');
if (oauthErr) {
throw new Error(`OpenRouter OAuth failed: ${oauthErr}`);
} Defensive patterns
Strategy: validation
Validate before calling
const params = new URL(callbackUrl).searchParams;
const failedOrDenied = params.has('error') || !params.has('code');
if (failedOrDenied) {
// surface as cancellation with params.get('error') detail; do not call extractOAuthCode
} Prevention
- Check the error query parameter before treating a missing code as exceptional
- Present access_denied as 'cancelled' in the UI, not an error dialog
- Validate client_id and scopes before opening the authorize URL
When it happens
Trigger: User clicks Deny on OpenRouter's consent screen; OpenRouter redirects with ?error=access_denied; a malformed client_id or scope produces an error redirect instead of a code.
Common situations: User denies the requested scopes; upstream app credentials misconfigured; scope string typo; user navigates back mid-flow.
Related errors
- OpenRouter OAuth returned an invalid callback URL.
- OpenRouter OAuth callback state did not match the request.
- OpenRouter key exchange failed (${response.status}).
- OpenRouter key exchange succeeded but no API key was returne
- OpenRouter OAuth listener returned an invalid redirect URL.
AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16).
Data as JSON: /api/errors/76bc0f8493623709.
Report an issue: GitHub.