{"record":{"id":"f827311a7f41ac86","repo":"decolua/9router","slug":"install-failed","errorCode":null,"errorMessage":"Install failed","messagePattern":"Install failed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/app/(dashboard)/dashboard/token-saver/TokenSaverClient.js","lineNumber":254,"sourceCode":"      logPollRef.current = null;\n    }\n  }, []);\n\n  useEffect(() => () => stopLogPolling(), [stopLogPolling]);\n\n  const installExtrasConfirmed = useCallback(async () => {\n    if (pendingExtras.length === 0) return;\n    setExtrasActionLoading(true);\n    setExtrasActionError(\"\");\n    startLogPolling();\n    try {\n      const res = await fetch(\"/api/headroom/extras\", {\n        method: \"POST\",\n        headers: { \"Content-Type\": \"application/json\" },\n        body: JSON.stringify({ extras: pendingExtras }),\n      });\n      const data = await res.json().catch(() => ({}));\n      if (!res.ok) throw new Error(data.error || \"Install failed\");\n      setHeadroomExtras((s) => ({\n        ...s,\n        version: data.version ?? s.version,\n        extras: data.extras || s.extras,\n      }));\n      setPendingExtras([]);\n    } catch (e) {\n      setExtrasActionError(e.message);\n    } finally {\n      stopLogPolling();\n      setExtrasActionLoading(false);\n    }\n  }, [pendingExtras, startLogPolling, stopLogPolling]);\n\n  const removeExtraConfirmed = useCallback(async (extra) => {\n    setRemovingExtra(extra);\n    setExtrasActionError(\"\");\n    startLogPolling();","sourceCodeStart":236,"sourceCodeEnd":272,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/app/(dashboard)/dashboard/token-saver/TokenSaverClient.js#L236-L272","documentation":"The extras install handler throws this when POST /api/headroom/extras with {extras: pendingExtras} responds non-2xx without a specific `error` field. It means the optional headroom extras (code/ml) failed to install; the message is shown via headroomActionError.","triggerScenarios":"POST /api/headroom/extras returns non-2xx: package download fails (no network/registry unreachable), install script lacks permission, unsupported platform/binary unavailable, or response body is not JSON (res.json() catch → {} so data.error is undefined).","commonSituations":"Offline or corporate proxy blocking the package registry; installing on a platform without prebuilt binaries; disk full or permission denied writing to the extras directory; requesting extras names the server doesn't recognize.","solutions":["Inspect the POST response status/body for the server's real install error","Verify network access to the package registry from the server host","Check the extras install directory permissions and free disk space","Confirm the requested extras names (code/ml) are supported on this platform/server version"],"exampleFix":"// before\nif (!res.ok) throw new Error(data.error || \"Install failed\");\n// after\nif (!res.ok) throw new Error(data.error || `Install failed (HTTP ${res.status})`);","handlingStrategy":"try-catch","validationCode":"// only request known extras\nconst allowed = [\"code\", \"ml\"];\nconst extras = pendingExtras.filter(x => allowed.includes(x));\nif (!extras.length) return;","typeGuard":"const isKnownExtra = (x) => x === \"code\" || x === \"ml\";","tryCatchPattern":"try {\n  const res = await fetch(\"/api/headroom/extras\", { method: \"POST\", body: JSON.stringify({ extras: pendingExtras }) });\n  const data = await res.json().catch(() => ({}));\n  if (!res.ok) throw new Error(data.error || `Install failed (HTTP ${res.status})`);\n  setPendingExtras([]);\n} catch (e) {\n  setHeadroomActionError(e.message);\n}","preventionTips":["Verify network/registry access from the server before installing","Check disk space and write permissions on the extras directory","Only install extras supported by the current platform build","Read the server response body to distinguish download vs permission failures"],"tags":["http","install","headroom","dashboard"],"backgroundTag":"http-request-failed","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}