zeroclaw-labs/zeroclaw · error · anyhow::Error
OpenAI device-code polling failed ({status}): {text}
Error message
OpenAI device-code polling failed ({status}): {text} What it means
The catch-all branch of poll_device_code_tokens: the token poll returned a non-success status AND the body did not parse as a known OAuth error JSON (or was not an error object at all), so the raw response text is embedded in the message. This is how unexpected responses — 5xx HTML pages, gateway timeouts, proxy blocks, empty bodies — surface from the device flow.
Source
Thrown at crates/zeroclaw-providers/src/auth/openai_oauth.rs:224
interval_secs = interval_secs.saturating_add(5);
continue;
}
"access_denied" => {
anyhow::bail!("OpenAI device-code authorization was denied")
}
"expired_token" => {
anyhow::bail!("OpenAI device-code expired")
}
_ => {
anyhow::bail!(
"OpenAI device-code polling failed ({status}): {}",
err.error_description.unwrap_or(err.error)
)
}
}
}
anyhow::bail!("OpenAI device-code polling failed ({status}): {text}");
}
}
pub async fn receive_loopback_code(expected_state: &str, timeout: Duration) -> Result<String> {
::zeroclaw_log::scope!(
model_provider_type: "openai",
model_provider_alias: "oauth",
=> async move {
receive_loopback_code_inner(expected_state, timeout).await
}
)
.await
}
async fn receive_loopback_code_inner(expected_state: &str, timeout: Duration) -> Result<String> {
let listener = TcpListener::bind("127.0.0.1:1455")
.await
.context("Failed to bind callback listener at 127.0.0.1:1455")?;View on GitHub (pinned to 88bb9c8533)
Solutions
- Inspect {status} and {text}: HTML/markup usually means proxy or incident page — check egress and https://status.openai.com
- Retry the login after the incident clears; the device flow is stateless to restart
- If a proxy is intercepting, add an exception for the OpenAI auth domains or use the browser loopback flow
Defensive patterns
Strategy: retry
Try / catch
let mut attempt = 0;
loop {
match openai_oauth::poll_device_code_tokens(&client, &device).await {
Err(e) if e.to_string().contains("polling failed") && attempt < 3 => {
attempt += 1;
tokio::time::sleep(Duration::from_secs(2u64.pow(attempt))).await;
}
other => break other,
}
} Prevention
- Ensure direct egress to OpenAI auth domains; bypass intercepting proxies
- Treat HTML bodies in this error as infrastructure noise, not OAuth failure
- Check vendor status before retrying during incidents
When it happens
Trigger: OpenAI token endpoint returning 500/502/503 with an HTML or empty body during an incident, or a corporate proxy/captive portal intercepting the POST during device-code login.
Common situations: Cloud/vendor incidents, rate-limit pages without JSON bodies, TLS-inspecting proxies rewriting responses, or DNS sinkholes.
Related errors
- OpenAI device-code start failed ({status}): {body}
- OpenAI device-code polling failed ({status}): {}
- elicitation returned unknown choice const: {s}
- Device-code flow timed out before authorization completed
- OpenAI device-code authorization was denied
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/916f81e0cca38c8f.
Report an issue: GitHub.