zeroclaw-labs/zeroclaw · warning · anyhow::Error

Device-code flow timed out before authorization completed

Error message

Device-code flow timed out before authorization completed

What it means

poll_device_code_tokens loops, sleeping device.interval seconds between token polls, and checks elapsed time against device.expires_in from the device-code response. If the user has not completed authorization before that lifetime elapses, it bails with this timeout. The device code is single-use and short-lived (typically a few minutes), so a late approval cannot be salvaged.

Source

Thrown at crates/zeroclaw-providers/src/auth/openai_oauth.rs:175

        user_code: parsed.user_code,
        verification_uri: parsed.verification_uri,
        verification_uri_complete: parsed.verification_uri_complete,
        expires_in: parsed.expires_in,
        interval: parsed.interval.unwrap_or(5).max(1),
        message: parsed.message,
    })
}

pub async fn poll_device_code_tokens(
    client: &Client,
    device: &DeviceCodeStart,
) -> Result<TokenSet> {
    let started = Instant::now();
    let mut interval_secs = device.interval.max(1);

    loop {
        if started.elapsed() > Duration::from_secs(device.expires_in) {
            anyhow::bail!("Device-code flow timed out before authorization completed");
        }

        tokio::time::sleep(Duration::from_secs(interval_secs)).await;

        let form = [
            ("grant_type", "urn:ietf:params:oauth:grant-type:device_code"),
            ("device_code", device.device_code.as_str()),
            ("client_id", OPENAI_OAUTH_CLIENT_ID),
        ];

        let response = client
            .post(OPENAI_OAUTH_TOKEN_URL)
            .form(&form)
            .send()
            .await
            .context("Failed polling OpenAI device-code token endpoint")?;

        if response.status().is_success() {

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Restart the flow: run `zeroclaw auth login --model-provider openai-codex --device-code` again and authorize promptly at the verification URL
  2. Copy the user_code immediately — it expires with the device code
  3. If approval is slow by nature (approval queues), use the browser loopback flow instead so the redirect arrives when ready
Defensive patterns

Strategy: retry

Try / catch

match openai_oauth::poll_device_code_tokens(&client, &device).await {
    Err(e) if e.to_string().contains("timed out before authorization") => {
        // restart the whole flow with a fresh device code
        start_device_code_flow(&client, &id, &secret, &scopes).await
    }
    other => other,
}

Prevention

When it happens

Trigger: Starting `--device-code` login, receiving the user_code/URL, and not visiting/authorizing it before expires_in; or the user approving after the poll loop already exited.

Common situations: Headless/servers where the URL is copy-pasted slowly, user steps away from the terminal, screen-sharing sessions with delays, or automation that surfaces the code too late.

Understand the failure class

Related errors


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