{"record":{"id":"1a5d85195f250e21","repo":"jlcodes99/cockpit-tools","slug":"codex-oauth-login-id-callback-url-el-1a5d85","errorCode":null,"errorMessage":"Codex OAuth 回调超时: login_id={}, callback_url={}, elapsed={}s","messagePattern":"Codex OAuth 回调超时: login_id=(.+?), callback_url=(.+?), elapsed=(.+?)s","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/src/modules/codex_oauth.rs","lineNumber":858,"sourceCode":"    loop {\n        let should_stop = {\n            let oauth_state = OAUTH_STATE.lock().unwrap();\n            match oauth_state.as_ref() {\n                Some(state) => state.state != expected_state || state.login_id != expected_login_id,\n                None => true,\n            }\n        };\n\n        if should_stop {\n            logger::log_info(&format!(\n                \"Codex OAuth 已取消或状态已变更，停止回调监听: login_id={}\",\n                expected_login_id\n            ));\n            break;\n        }\n\n        if start.elapsed() > timeout {\n            logger::log_error(&format!(\n                \"Codex OAuth 回调超时: login_id={}, callback_url={}, elapsed={}s\",\n                expected_login_id,\n                callback_url,\n                start.elapsed().as_secs()\n            ));\n            clear_state_on_exit = true;\n            break;\n        }\n\n        if let Ok(Some(request)) = server.try_recv() {\n            let url = request.url().to_string();\n\n            if url.starts_with(\"/auth/callback\") {\n                let has_query = url.contains('?');\n                logger::log_info(&format!(\n                    \"Codex OAuth 收到回调请求: login_id={}, path=/auth/callback, has_query={}\",\n                    expected_login_id, has_query\n                ));","sourceCodeStart":840,"sourceCodeEnd":876,"githubUrl":"https://github.com/jlcodes99/cockpit-tools/blob/1ed8b77992d62ca81fabf744deb0839ad361d5bf/src-tauri/src/modules/codex_oauth.rs#L840-L876","documentation":"start_callback_server waits a bounded time for the browser to complete the Codex OAuth redirect. If the expected callback (with matching state/login_id) does not arrive before `timeout` elapses, it logs this message, sets clear_state_on_exit=true, and aborts the login session. It is the deliberate timeout path of the local OAuth flow, not an unexpected crash.","triggerScenarios":"start_oauth_login (or ensure_callback_listener_for_state) started the listener, but the user never completed the browser consent, the redirect URL/port mismatched, or the callback POST/GET was dropped — so elapsed() > timeout fires.","commonSituations":"User leaves the consent tab open/pending or closes the browser; popup blocked so the auth page never opens; redirect_uri registered in the Codex OAuth app points to a different port so the local server never matches the state; system clock/sleep (laptop suspended) consuming the window; very short timeout configured.","solutions":["Retry the login and complete the browser consent within the timeout window","Verify the redirect_uri/port used in the auth URL matches what start_callback_server listens on","Ensure popups/external browser are allowed to open the authorization URL","Check that the state/login_id is not being cleared by a concurrent login attempt","Increase the timeout if slow networks or multi-account switching are common"],"exampleFix":"// before\nif start.elapsed() > timeout {\n    logger::log_error(&format!(\"Codex OAuth 回调超时: ...\"));\n    clear_state_on_exit = true;\n}\n// after\nif start.elapsed() > timeout {\n    logger::log_error(&format!(\"Codex OAuth 回调超时: ...\"));\n    clear_state_on_exit = true;\n    // surface to caller so the UI can prompt a retry\n    return Err(format!(\"Codex OAuth 登录超时 ({}s)，请重试\", timeout.as_secs()));\n}","handlingStrategy":"retry","validationCode":"// Before starting OAuth, verify a browser can open the auth URL and redirect_uri matches\nassert_eq!(redirect_uri, format!(\"http://127.0.0.1:{}\", port));\n// and check no other login session holds the same state\nif oauth_state_exists() { clear_oauth_state_if_matches(&state, &login_id); }","typeGuard":null,"tryCatchPattern":"match start_oauth_login(app_handle).await {\n    Err(e) if e.contains(\"超时\") => {\n        // prompt user: 'Login timed out — please retry and complete consent in the browser'\n        retry_login_with_fresh_state()\n    }\n    Ok(r) => handle_response(r),\n    Err(e) => eprintln!(\"{}\", e),\n}","preventionTips":["Show a visible countdown/deadline in the UI so users complete consent in time","Always restart the full flow (new state/verifier) after a timeout — never reuse state","Make sure the browser/auth page actually opened (handle popup blockers)","Keep the machine awake during login; increase timeout for slow networks"],"tags":["oauth","timeout","callback","tauri"],"backgroundTag":"oauth-callback-timeout","analyzedSha":"1ed8b77992d62ca81fabf744deb0839ad361d5bf","analyzedAt":"2026-09-05T09:51:41.178Z","contentChangedAt":"2026-09-05T09:51:41.178Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}