decolua/9router · error · Error

Install failed

Error message

Install failed

What it means

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.

Source

Thrown at src/app/(dashboard)/dashboard/token-saver/TokenSaverClient.js:254

      logPollRef.current = null;
    }
  }, []);

  useEffect(() => () => stopLogPolling(), [stopLogPolling]);

  const installExtrasConfirmed = useCallback(async () => {
    if (pendingExtras.length === 0) return;
    setExtrasActionLoading(true);
    setExtrasActionError("");
    startLogPolling();
    try {
      const res = await fetch("/api/headroom/extras", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ extras: pendingExtras }),
      });
      const data = await res.json().catch(() => ({}));
      if (!res.ok) throw new Error(data.error || "Install failed");
      setHeadroomExtras((s) => ({
        ...s,
        version: data.version ?? s.version,
        extras: data.extras || s.extras,
      }));
      setPendingExtras([]);
    } catch (e) {
      setExtrasActionError(e.message);
    } finally {
      stopLogPolling();
      setExtrasActionLoading(false);
    }
  }, [pendingExtras, startLogPolling, stopLogPolling]);

  const removeExtraConfirmed = useCallback(async (extra) => {
    setRemovingExtra(extra);
    setExtrasActionError("");
    startLogPolling();

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Inspect the POST response status/body for the server's real install error
  2. Verify network access to the package registry from the server host
  3. Check the extras install directory permissions and free disk space
  4. Confirm the requested extras names (code/ml) are supported on this platform/server version

Example fix

// before
if (!res.ok) throw new Error(data.error || "Install failed");
// after
if (!res.ok) throw new Error(data.error || `Install failed (HTTP ${res.status})`);
Defensive patterns

Strategy: try-catch

Validate before calling

// only request known extras
const allowed = ["code", "ml"];
const extras = pendingExtras.filter(x => allowed.includes(x));
if (!extras.length) return;

Type guard

const isKnownExtra = (x) => x === "code" || x === "ml";

Try / catch

try {
  const res = await fetch("/api/headroom/extras", { method: "POST", body: JSON.stringify({ extras: pendingExtras }) });
  const data = await res.json().catch(() => ({}));
  if (!res.ok) throw new Error(data.error || `Install failed (HTTP ${res.status})`);
  setPendingExtras([]);
} catch (e) {
  setHeadroomActionError(e.message);
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/f827311a7f41ac86. Report an issue: GitHub.