{"record":{"id":"29ff6637a710645f","repo":"lbjlaq/Antigravity-Manager","slug":"cloudflared-install-failed","errorCode":null,"errorMessage":"Cloudflared install failed","messagePattern":"Cloudflared install failed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/pages/ApiProxy.tsx","lineNumber":301,"sourceCode":"    };\n\n    // Cloudflared: 启动/停止\n    const handleCfToggle = async (enable: boolean) => {\n        if (enable && !status.running) {\n            showToast(\n                t('proxy.cloudflared.require_proxy_running', { defaultValue: 'Please start the local proxy service first' }),\n                'warning'\n            );\n            return;\n        }\n        setCfLoading(true);\n        try {\n            if (enable) {\n                if (!cfStatus.installed) {\n                    const installStatus = await invoke<typeof cfStatus>('cloudflared_install');\n                    setCfStatus(installStatus);\n                    if (!installStatus.installed) {\n                        throw new Error('Cloudflared install failed');\n                    }\n                    showToast(t('proxy.cloudflared.install_success', { defaultValue: 'Cloudflared installed successfully' }), 'success');\n                }\n\n                const config = {\n                    enabled: true,\n                    mode: cfMode,\n                    port: appConfig?.proxy.port || 8045,\n                    token: cfMode === 'auth' ? cfToken : null,\n                    use_http2: cfUseHttp2,\n                };\n                const status = await invoke<typeof cfStatus>('cloudflared_start', { config });\n                setCfStatus(status);\n                showToast(t('proxy.cloudflared.started', { defaultValue: 'Tunnel started' }), 'success');\n\n                // 持久化“启用”状态\n                if (appConfig) {\n                    const newConfig = {","sourceCodeStart":283,"sourceCodeEnd":319,"githubUrl":"https://github.com/lbjlaq/Antigravity-Manager/blob/a2e3c454237d6d6ef423dfe20505b1ac62803c7c/src/pages/ApiProxy.tsx#L283-L319","documentation":"handleCfToggle() in src/pages/ApiProxy.tsx:301 throws this when invoke('cloudflared_install') resolves successfully but the returned status object still has installed: false — i.e. the backend ran the install routine and reported failure without throwing (errors inside the install flow are folded into the status). In web mode this maps to POST /api/proxy/cloudflared/install (request.ts:103). The frontend then discards whatever reason the status carried and throws a bare string, losing the diagnostic.","triggerScenarios":"The backend cannot download the cloudflared binary (GitHub releases unreachable, GFW/firewall block, proxy needed but not configured); no write permission in the data/bin directory; unsupported OS/arch with no matching release artifact; checksum verification failure or partial download; disk full. Also any server-side install error that the manager converts into { installed: false, ... } instead of an HTTP error.","commonSituations":"Enabling the Cloudflare tunnel from behind a corporate firewall or in mainland China where github.com is slow/blocked; read-only or sandboxed app directory; CI/container environments without the build tools or permissions cloudflared's installer expects; toggling the tunnel on a fresh machine where cfStatus.installed is false so the implicit install runs first.","solutions":["Inspect the actual installStatus object (log it) and the backend logs — it typically carries an error/message/detail field naming the real cause (network, permission, arch).","Fix network reachability for the download host (configure system proxy, or pre-download the cloudflared binary for your OS/arch and place it where the manager looks for it, then re-toggle).","Ensure the app data/bin directory is writable and has disk space.","As a workaround, install cloudflared manually (package manager or GitHub release), then press install/refresh status again so cloudflared_get_status detects the existing binary."],"exampleFix":"// before\nconst installStatus = await invoke<typeof cfStatus>('cloudflared_install');\nsetCfStatus(installStatus);\nif (!installStatus.installed) {\n    throw new Error('Cloudflared install failed');\n}\n\n// after: keep the backend's reason instead of a bare string\nif (!installStatus.installed) {\n    const reason = (installStatus as any).error || (installStatus as any).message || 'unknown reason (check network / GitHub reachability / write permissions)';\n    throw new Error(`Cloudflared install failed: ${reason}`);\n}","handlingStrategy":"retry","validationCode":"// Check status fields before treating the install as a hard failure\nconst installStatus = await invoke<Record<string, unknown>>('cloudflared_install');\nconst reason = (installStatus.error ?? installStatus.message ?? installStatus.detail) as string | undefined;\nconst transient = typeof reason === 'string' && /timeout|network|temporarily/i.test(reason);\nif (!installStatus.installed && transient) {\n  // one retry is reasonable for flaky downloads; surface reason otherwise\n}","typeGuard":"type CloudflaredStatus = { installed: boolean; error?: string; message?: string; detail?: string };\nconst isInstallFailure = (s: unknown): s is CloudflaredStatus & { installed: false } =>\n  typeof s === 'object' && s !== null && (s as CloudflaredStatus).installed !== true;","tryCatchPattern":"let installStatus = await invoke('cloudflared_install');\nif (!installStatus.installed) {\n  const reason = installStatus.error || installStatus.message || 'check network / GitHub reachability';\n  if (/timeout|network|temporarily/i.test(String(reason))) {\n    await new Promise(r => setTimeout(r, 2000));\n    installStatus = await invoke('cloudflared_install'); // single retry for transient download failure\n  }\n  if (!installStatus.installed) throw new Error(`Cloudflared install failed: ${reason}`);\n}","preventionTips":["Don't discard the status payload: always read error/message/detail from the install result before throwing a generic string.","Pre-install cloudflared via package manager or a manually downloaded release in restricted-network environments; the status check will then skip the download.","Verify the app data/bin directory is writable and on a disk with free space before enabling the tunnel.","Watch the backend logs during cloudflared_install — the frontend error alone never carries the root cause."],"tags":["cloudflared","tunnel","install","network","binary-download"],"backgroundTag":"binary-download-install-failed","analyzedSha":"a2e3c454237d6d6ef423dfe20505b1ac62803c7c","analyzedAt":"2026-08-16T19:44:46.389Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}