decolua/9router · error · Error
Restart failed
Error message
Restart failed
What it means
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'.
Source
Thrown at src/app/(dashboard)/dashboard/token-saver/TokenSaverClient.js:333
variant: "danger",
onConfirm: () => removeExtraConfirmed(extra),
});
}, [removeExtraConfirmed]);
// Toggle an extra's active state (persist setting), then restart the proxy so
// the new --code-aware / --disable-kompress flags take effect.
const toggleExtraActive = useCallback(async (extra, value) => {
setExtrasActionError("");
if (extra === "code") setCodeAware(value);
if (extra === "ml") setKompress(value);
const key = extra === "code" ? "headroomCodeAware" : "headroomKompress";
await patchSetting({ [key]: value });
if (!headroomStatus.running) return;
setRestartingProxy(true);
try {
const res = await fetch("/api/headroom/restart", { method: "POST" });
const data = await res.json().catch(() => ({}));
if (!res.ok) throw new Error(data.error || "Restart failed");
await refreshHeadroomStatus();
} catch (e) {
setExtrasActionError(e.message);
} finally {
setRestartingProxy(false);
}
}, [headroomStatus.running, refreshHeadroomStatus]);
const handleCavemanLevel = (level) => {
setCavemanLevel(level);
patchSetting({ cavemanLevel: level });
};
const handlePonytailEnabled = (value) => {
setPonytailEnabled(value);
patchSetting({ ponytailEnabled: value });
};
View on GitHub (pinned to 90b52e06ff)
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.
Example fix
// before
if (!res.ok) throw new Error(data.error || "Restart failed");
// after
if (!res.ok) throw new Error(data.code === "EXTERNAL_PROXY" ? "External proxies cannot be restarted from the dashboard" : data.error || `Restart failed (HTTP ${res.status})`); Defensive patterns
Strategy: validation
Validate before calling
const status = await fetch("/api/headroom/status").then((r) => r.json()).catch(() => null);
if (!status?.installed) throw new Error("Headroom not installed — restart unavailable");
if (status.url && !/^https?:\/\/(localhost|127\.0\.0\.1)/.test(status.url)) throw new Error("External proxy — restart it outside 9Router"); Type guard
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; } }; Try / catch
try {
const res = await fetch("/api/headroom/restart", { method: "POST" });
const data = await res.json().catch(() => ({}));
if (!res.ok) {
if (data.code === "EXTERNAL_PROXY") throw new Error("Start external proxies yourself — dashboard restart is loopback-only");
if (data.code === "NOT_INSTALLED") throw new Error("Install Headroom first");
throw new Error(data.error || `Restart failed (HTTP ${res.status})`);
}
} catch (e) {
showRestartError(e.message);
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- Failed to start proxy
- PXPIPE ${endpoint} failed
- Machine ID is required for Cursor API
- http2 module not available
- Kiro tool input changed fragment type
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/4cd7f1c736dd0fc2.
Report an issue: GitHub.