{"record":{"id":"8a11e20442231041","repo":"warpdotdev/warp","slug":"unexpected-non-terminal-oauth-status-returned-8a11e2","errorCode":null,"errorMessage":"Unexpected non-terminal OAuth status returned","messagePattern":"Unexpected non-terminal OAuth status returned","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"app/src/ai/agent_sdk/integration.rs","lineNumber":322,"sourceCode":"                                            }\n                                            Ok(OauthConnectTxStatus::Failed) => {\n                                                ctx.terminate_app(\n                                                    TerminationMode::ForceTerminate,\n                                                    Some(Err(anyhow::anyhow!(\"OAuth authorization failed.\"))),\n                                                );\n                                            }\n                                            Ok(OauthConnectTxStatus::Expired) => {\n                                                ctx.terminate_app(\n                                                    TerminationMode::ForceTerminate,\n                                                    Some(Err(anyhow::anyhow!(\"OAuth authorization expired.\"))),\n                                                );\n                                            }\n                                            Ok(OauthConnectTxStatus::Pending)\n                                            | Ok(OauthConnectTxStatus::InProgress) => {\n                                                // Should not be returned by poll_oauth_until_terminal.\n                                                ctx.terminate_app(\n                                                    TerminationMode::ForceTerminate,\n                                                    Some(Err(anyhow::anyhow!(\"Unexpected non-terminal OAuth status returned\"))),\n                                                );\n                                            }\n                                            Err(err) => {\n                                                ctx.terminate_app(\n                                                    TerminationMode::ForceTerminate,\n                                                    Some(Err(anyhow::anyhow!(\"Error polling OAuth status: {err}\"))),\n                                                );\n                                            }\n                                        }\n                                    },\n                                );\n                            }\n                            (Some(auth_url), None) => {\n                                println!(\"Authorize the provider here: {auth_url}\\n\");\n                                ctx.open_url(&auth_url);\n                                println!(\n                                    \"After authorizing, re-run the command to continue the integration {action} process.\",\n                                );","sourceCodeStart":304,"sourceCodeEnd":340,"githubUrl":"https://github.com/warpdotdev/warp/blob/e72fd7aacbbb2236d9b3be2aad7e7178fe94b4bc/app/src/ai/agent_sdk/integration.rs#L304-L340","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nlet status = client.get_oauth_tx(tx_id).await?;\nif status != OauthConnectTxStatus::InProgress {\n    return Ok(status);\n}\n// after\nlet status = client.get_oauth_tx(tx_id).await?;\nmatch status {\n    OauthConnectTxStatus::Pending | OauthConnectTxStatus::InProgress => continue,\n    terminal => return Ok(terminal),\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"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.","preventionTips":["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."],"tags":["oauth","integration","agent-sdk","invariant","rust"],"backgroundTag":null,"analyzedSha":"e72fd7aacbbb2236d9b3be2aad7e7178fe94b4bc","analyzedAt":"2026-08-16T08:27:25.381Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}