{"record":{"id":"5818fbfe4de5131f","repo":"jlcodes99/cockpit-tools","slug":"cloud-code-access-forbidden","errorCode":null,"errorMessage":"Cloud Code access forbidden","messagePattern":"Cloud Code access forbidden","errorType":"exception","errorClass":null,"httpStatus":403,"severity":"error","filePath":"src-tauri/src/modules/wakeup.rs","lineNumber":536,"sourceCode":"                                        ));\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);\n                            if delay > 0 {\n                                crate::modules::logger::log_info(&format!(\n                                    \"[Wakeup] 准备重试: delay={}ms\",\n                                    delay","sourceCodeStart":518,"sourceCodeEnd":554,"githubUrl":"https://github.com/jlcodes99/cockpit-tools/blob/1ed8b77992d62ca81fabf744deb0839ad361d5bf/src-tauri/src/modules/wakeup.rs#L518-L554","documentation":"In send_stream_request, an HTTP 403 FORBIDDEN from the Cloud Code endpoint is converted into the error string 'Cloud Code access forbidden'. Unlike 401 (authentication), 403 means the server recognized the caller but denies permission for this operation or resource, so retrying with the same credentials will not help.","triggerScenarios":"trigger_wakeup_direct → send_stream_request gets a 403 response while calling the wakeup/stream API with a valid but insufficiently-privileged access_token.","commonSituations":"Account/plan lacks the Cloud Code feature; admin disabled the integration; token scopes too narrow; IP/region or workspace allowlist blocks the client; user was removed from the required team/organization.","solutions":["Confirm the account has Cloud Code permission enabled (plan/admin settings)","Re-authenticate so the token includes required scopes","Check workspace/organization membership and any IP or region restrictions","Ask the service admin to unblock the client if a policy/allowlist is in effect","Distinguish 403 from 401 in client handling — do not retry with the same token"],"exampleFix":"// before\nif status == reqwest::StatusCode::FORBIDDEN {\n    return Err(\"Cloud Code access forbidden\".to_string());\n}\n// after\nif status == reqwest::StatusCode::FORBIDDEN {\n    return Err(format!(\"Cloud Code access forbidden (account lacks permission; check plan/scopes for base {})\", base));\n}","handlingStrategy":"fallback","validationCode":"// Pre-flight permission check via a cheap authenticated endpoint before wakeup\nlet me = client.get(format!(\"{base}/me\")).bearer_auth(token).send().await?;\nif me.status() == reqwest::StatusCode::FORBIDDEN {\n    eprintln!(\"account lacks Cloud Code permission; aborting wakeup\");\n}","typeGuard":"fn is_access_forbidden(err: &str) -> bool { err == \"Cloud Code access forbidden\" }","tryCatchPattern":"match trigger_wakeup_direct(...).await {\n    Err(e) if e == \"Cloud Code access forbidden\" => {\n        // do NOT retry with same token; disable wakeup and surface a permission message\n        disable_wakeup_with_reason(\"Cloud Code access denied for this account\");\n    }\n    other => handle(other),\n}","preventionTips":["Check plan/admin permissions once at login, not on every wakeup","Request all required scopes at authorization time","Never treat 403 as retryable — alert the user instead","Keep workspace/organization membership and IP allowlists up to date"],"tags":["http-403","authz","permissions","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"}