{"record":{"id":"4cd7f1c736dd0fc2","repo":"decolua/9router","slug":"restart-failed","errorCode":null,"errorMessage":"Restart failed","messagePattern":"Restart failed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/app/(dashboard)/dashboard/token-saver/TokenSaverClient.js","lineNumber":333,"sourceCode":"      variant: \"danger\",\n      onConfirm: () => removeExtraConfirmed(extra),\n    });\n  }, [removeExtraConfirmed]);\n\n  // Toggle an extra's active state (persist setting), then restart the proxy so\n  // the new --code-aware / --disable-kompress flags take effect.\n  const toggleExtraActive = useCallback(async (extra, value) => {\n    setExtrasActionError(\"\");\n    if (extra === \"code\") setCodeAware(value);\n    if (extra === \"ml\") setKompress(value);\n    const key = extra === \"code\" ? \"headroomCodeAware\" : \"headroomKompress\";\n    await patchSetting({ [key]: value });\n    if (!headroomStatus.running) return;\n    setRestartingProxy(true);\n    try {\n      const res = await fetch(\"/api/headroom/restart\", { method: \"POST\" });\n      const data = await res.json().catch(() => ({}));\n      if (!res.ok) throw new Error(data.error || \"Restart failed\");\n      await refreshHeadroomStatus();\n    } catch (e) {\n      setExtrasActionError(e.message);\n    } finally {\n      setRestartingProxy(false);\n    }\n  }, [headroomStatus.running, refreshHeadroomStatus]);\n\n  const handleCavemanLevel = (level) => {\n    setCavemanLevel(level);\n    patchSetting({ cavemanLevel: level });\n  };\n\n  const handlePonytailEnabled = (value) => {\n    setPonytailEnabled(value);\n    patchSetting({ ponytailEnabled: value });\n  };\n","sourceCodeStart":315,"sourceCodeEnd":351,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/app/(dashboard)/dashboard/token-saver/TokenSaverClient.js#L315-L351","documentation":"After patching a Headroom setting, TokenSaverClient restarts the local Headroom proxy via POST /api/headroom/restart. The route rejects with 400 when the configured headroomUrl is not loopback (EXTERNAL_PROXY — external proxies must be started outside 9Router) or when the proxy is NOT_INSTALLED, and 500 for spawn/process errors; the client surfaces data.error or the generic 'Restart failed'.","triggerScenarios":"POST /api/headroom/restart returns non-OK: 400 EXTERNAL_PROXY when settings.headroomUrl points at a non-loopback host, 400 NOT_INSTALLED when the Headroom binary/venv is missing, or 500 when restartHeadroomProxy fails to spawn the process on the parsed port (default 8787).","commonSituations":"User configured a remote/external Headroom URL then clicked restart; Headroom was never installed; port 8787 occupied or spawn failure; dashboard session stale after the server was updated.","solutions":["Verify the Headroom proxy is installed for the current user; install it from the token-saver dashboard before restarting.","Check the headroomUrl setting — only http://127.0.0.1 or http://localhost URLs can be restarted from here; start external proxies yourself.","Confirm port 8787 (or the port in headroomUrl) is free and not blocked.","Inspect the API response's error/code fields (e.g. NOT_INSTALLED, EXTERNAL_PROXY) rather than the generic message to target the fix."],"exampleFix":"// before\nif (!res.ok) throw new Error(data.error || \"Restart failed\");\n// after\nif (!res.ok) throw new Error(data.code === \"EXTERNAL_PROXY\" ? \"External proxies cannot be restarted from the dashboard\" : data.error || `Restart failed (HTTP ${res.status})`);","handlingStrategy":"validation","validationCode":"const status = await fetch(\"/api/headroom/status\").then((r) => r.json()).catch(() => null);\nif (!status?.installed) throw new Error(\"Headroom not installed — restart unavailable\");\nif (status.url && !/^https?:\\/\\/(localhost|127\\.0\\.0\\.1)/.test(status.url)) throw new Error(\"External proxy — restart it outside 9Router\");","typeGuard":"const isLoopbackUrl = (u) => { try { const url = new URL(u); return [\"http:\", \"https:\"].includes(url.protocol) && [\"localhost\", \"127.0.0.1\", \"::1\"].includes(url.hostname); } catch { return false; } };","tryCatchPattern":"try {\n  const res = await fetch(\"/api/headroom/restart\", { method: \"POST\" });\n  const data = await res.json().catch(() => ({}));\n  if (!res.ok) {\n    if (data.code === \"EXTERNAL_PROXY\") throw new Error(\"Start external proxies yourself — dashboard restart is loopback-only\");\n    if (data.code === \"NOT_INSTALLED\") throw new Error(\"Install Headroom first\");\n    throw new Error(data.error || `Restart failed (HTTP ${res.status})`);\n  }\n} catch (e) {\n  showRestartError(e.message);\n}","preventionTips":["Keep headroomUrl on loopback if you want in-dashboard restart.","Install Headroom before enabling its settings.","Check the response code field, not just the message, to disambiguate causes."],"tags":["proxy","process-management","ui-error"],"backgroundTag":"proxy-restart-failed","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}