zeroclaw-labs/zeroclaw · error

User denied authorization

Error message

User denied authorization

What it means

While polling the Google token endpoint during the device flow, the response carried error=access_denied. Google returns this when the user actively rejects the consent prompt (clicks Cancel/Deny) or when the app is blocked from consent. The poll loop maps that specific error code to this bail, ending the flow immediately.

Source

Thrown at crates/zeroclaw-providers/src/auth/gemini_oauth.rs:307

            return Ok(TokenSet {
                access_token: token_response.access_token,
                refresh_token: token_response.refresh_token,
                id_token: token_response.id_token,
                expires_at,
                token_type: token_response.token_type.or_else(|| Some("Bearer".into())),
                scope: token_response.scope,
            });
        }

        if let Ok(err) = serde_json::from_str::<OAuthErrorResponse>(&body) {
            match err.error.as_str() {
                "authorization_pending" => {}
                "slow_down" => {
                    tokio::time::sleep(Duration::from_secs(5)).await;
                }
                "access_denied" => {
                    anyhow::bail!("User denied authorization");
                }
                "expired_token" => {
                    anyhow::bail!("Device code expired");
                }
                _ => {
                    anyhow::bail!(
                        "Google OAuth error: {} - {}",
                        err.error,
                        err.error_description.unwrap_or_default()
                    );
                }
            }
        }
    }
}

/// Receive OAuth code via loopback callback OR manual stdin input.
/// If the callback server can't receive the redirect (e.g., remote/headless environment),

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Re-run auth login --device-code and click Allow/Continue on the consent screen this time
  2. If the app is unverified, complete OAuth verification or add the test user to the Google Cloud consent screen
  3. If a workspace policy blocks the app, use an account outside that workspace or ask the admin to allowlist the client id
Defensive patterns

Strategy: try-catch

Try / catch

match poll_device_code_tokens(client, id, secret, &device).await {
    Ok(tokens) => tokens,
    Err(e) if e.to_string() == "User denied authorization" => {
        eprintln!("consent was denied; re-run the login and click Allow");
        return Err(e);
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: poll_device_code_tokens receives an OAuthErrorResponse whose error field is exactly "access_denied" after the user clicked deny on the Google consent screen, or after an admin policy auto-denied the OAuth client.

Common situations: User changes their mind at the consent screen; the Google Cloud OAuth app is unverified and the user backs out of the warning screen; a workspace admin disabled third-party access so consent is denied automatically.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/46b4777cc0738b19. Report an issue: GitHub.