{"record":{"id":"50d1c01d658f05bf","repo":"jlcodes99/cockpit-tools","slug":"authorization-expired","errorCode":null,"errorMessage":"Authorization expired","messagePattern":"Authorization expired","errorType":"exception","errorClass":null,"httpStatus":401,"severity":"error","filePath":"src-tauri/src/modules/wakeup.rs","lineNumber":532,"sourceCode":"                                    if delay > 0 {\n                                        crate::modules::logger::log_info(&format!(\n                                            \"[Wakeup] 准备重试: delay={}ms\",\n                                            delay\n                                        ));\n                                        sleep_with_cancel(\n                                            std::time::Duration::from_millis(delay),\n                                            cancel_rx,\n                                        )\n                                        .await?;\n                                    }\n                                    continue;\n                                }\n                            }\n                        }\n                    } else {\n                        if status == reqwest::StatusCode::UNAUTHORIZED {\n                            crate::modules::logger::log_error(\"[Wakeup] 授权失效 (401)\");\n                            return Err(\"Authorization expired\".to_string());\n                        }\n                        if status == reqwest::StatusCode::FORBIDDEN {\n                            crate::modules::logger::log_error(\"[Wakeup] 无权限 (403)\");\n                            return Err(\"Cloud Code access forbidden\".to_string());\n                        }\n                        let text = await_with_cancel(cancel_rx, res.text())\n                            .await?\n                            .unwrap_or_default();\n                        let retryable = status == reqwest::StatusCode::TOO_MANY_REQUESTS\n                            || status.as_u16() >= 500;\n                        let message = format!(\"唤醒请求失败: {} - {}\", status, text);\n                        last_error = Some(message.clone());\n                        crate::modules::logger::log_warn(&format!(\n                            \"[Wakeup] 请求失败: url={}, status={}, retryable={}\",\n                            url, status, retryable\n                        ));\n                        if retryable && attempt < DEFAULT_ATTEMPTS {\n                            let delay = get_backoff_delay_ms(attempt + 1);","sourceCodeStart":514,"sourceCodeEnd":550,"githubUrl":"https://github.com/jlcodes99/cockpit-tools/blob/1ed8b77992d62ca81fabf744deb0839ad361d5bf/src-tauri/src/modules/wakeup.rs#L514-L550","documentation":"send_stream_request calls the Cloud Code streaming endpoint with an access_token; on HTTP 401 UNAUTHORIZED it returns the fixed error string 'Authorization expired'. This means the bearer token was rejected — it is missing, malformed, or no longer valid — and the code deliberately stops retrying since retrying with the same token cannot succeed.","triggerScenarios":"trigger_wakeup_direct → send_stream_request receives a 401 from the wakeup/stream API while carrying the stored access_token.","commonSituations":"Token expired after long idle; refresh flow failed silently or never ran; user logged out/revoked access server-side; token for the wrong environment (staging vs prod base URL); clock skew making a valid token appear expired.","solutions":["Trigger a token refresh (or re-authentication) before the next wakeup attempt","Verify the access_token being attached is present and for the correct base URL/environment","Have the user log in again if refresh_token is missing or also revoked","Check device clock sync if tokens seem to expire instantly","Inspect base_url order — a 401 from one mirror may be environment-specific"],"exampleFix":"// before\nif status == reqwest::StatusCode::UNAUTHORIZED {\n    return Err(\"Authorization expired\".to_string());\n}\n// after\nif status == reqwest::StatusCode::UNAUTHORIZED {\n    if refresh_access_token().await.is_ok() {\n        continue; // retry with new token\n    }\n    return Err(\"Authorization expired\".to_string());\n}","handlingStrategy":"retry","validationCode":"// Pre-flight: ensure a token exists and refresh proactively if near expiry\nif access_token.is_empty() || token_expires_in_secs() < 60 {\n    refresh_access_token().await.expect(\"refresh failed; user must re-login\");\n}","typeGuard":"fn is_auth_expired(err: &str) -> bool { err == \"Authorization expired\" }","tryCatchPattern":"match trigger_wakeup_direct(...).await {\n    Err(e) if e == \"Authorization expired\" => {\n        if refresh_access_token().await.is_ok() {\n            retry_once().await; // single retry with fresh token\n        } else {\n            prompt_relogin();\n        }\n    }\n    other => handle(other),\n}","preventionTips":["Refresh tokens proactively before expiry (e.g. at 80% lifetime)","Handle refresh failures by prompting re-login instead of hammering 401s","Keep device clocks synchronized (NTP)","Persist tokens securely and per-environment to avoid cross-env 401s"],"tags":["http-401","auth","token-expired","network"],"backgroundTag":null,"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"}