warpdotdev/warp · error · anyhow::Error
Unexpected non-terminal OAuth status returned
Error message
Unexpected non-terminal OAuth status returned
What it means
Thrown after the OAuth polling helper `poll_oauth_until_terminal` resolves with `OauthConnectTxStatus::Pending` or `InProgress`. That helper is contractually required to keep polling until a terminal status (Completed/Failed/Expired), so a non-terminal result means an internal invariant broke. The app force-terminates (TerminationMode::ForceTerminate) rather than continue in an undefined state.
Source
Thrown at app/src/ai/agent_sdk/integration.rs:322
}
Ok(OauthConnectTxStatus::Failed) => {
ctx.terminate_app(
TerminationMode::ForceTerminate,
Some(Err(anyhow::anyhow!("OAuth authorization failed."))),
);
}
Ok(OauthConnectTxStatus::Expired) => {
ctx.terminate_app(
TerminationMode::ForceTerminate,
Some(Err(anyhow::anyhow!("OAuth authorization expired."))),
);
}
Ok(OauthConnectTxStatus::Pending)
| Ok(OauthConnectTxStatus::InProgress) => {
// Should not be returned by poll_oauth_until_terminal.
ctx.terminate_app(
TerminationMode::ForceTerminate,
Some(Err(anyhow::anyhow!("Unexpected non-terminal OAuth status returned"))),
);
}
Err(err) => {
ctx.terminate_app(
TerminationMode::ForceTerminate,
Some(Err(anyhow::anyhow!("Error polling OAuth status: {err}"))),
);
}
}
},
);
}
(Some(auth_url), None) => {
println!("Authorize the provider here: {auth_url}\n");
ctx.open_url(&auth_url);
println!(
"After authorizing, re-run the command to continue the integration {action} process.",
);View on GitHub (pinned to e72fd7aacb)
Solutions
- Inspect poll_oauth_until_terminal in app/src/ai/agent_sdk/integration.rs and confirm it only resolves on Completed/Failed/Expired.
- If a new OauthConnectTxStatus variant was added, classify it as non-terminal (keep polling) in the loop predicate.
- Re-run the integration command once — a transient server-side race can surface this branch.
- If unmodified code hits it, capture logs and file it as a client bug with the tx_id.
Example fix
// before
let status = client.get_oauth_tx(tx_id).await?;
if status != OauthConnectTxStatus::InProgress {
return Ok(status);
}
// after
let status = client.get_oauth_tx(tx_id).await?;
match status {
OauthConnectTxStatus::Pending | OauthConnectTxStatus::InProgress => continue,
terminal => return Ok(terminal),
} Defensive patterns
Strategy: retry
Try / catch
In the spawned callback, match the poll Result; on the Ok(Pending|InProgress) arm, re-enter start_create_or_update_flow once (bounded by the existing attempt counter) instead of terminating, and only ForceTerminate when the budget is exhausted.
Prevention
- Keep the terminal predicate in poll_oauth_until_terminal exhaustive over OauthConnectTxStatus — no wildcard arm that can return a non-terminal value.
- Add a unit test with a mocked non-terminal-only sequence asserting the future never resolves until a terminal status arrives.
- When adding a status variant, update the poll loop and this match arm in the same commit.
When it happens
Trigger: Running the agent-sdk `integration create/update` flow where the server returns both auth_url and tx_id: the client opens the browser, polls the transaction, and the polling future resolves successfully — not with Err — while the status is still Pending or InProgress. Typically a bug in poll_oauth_until_terminal's terminal predicate or a new enum variant it does not classify.
Common situations: A local refactor of poll_oauth_until_terminal that returns early; a newly added OauthConnectTxStatus variant the loop treats as final; a race where the poll loop's last response is non-final but the future completes anyway.
Related errors
- Error polling OAuth status: {err}
- Unexpected non-terminal OAuth status returned
- Timed out refreshing team metadata
- Timed out waiting for Warp Drive to sync
- Exceeded maximum number of authorization attempts ({}). Plea
AI-assisted analysis of warpdotdev/warp@e72fd7aacb (2026-08-16).
Data as JSON: /api/errors/8a11e20442231041.
Report an issue: GitHub.