JuliusBrussee/caveman · error · Error

${profile.display_name} integration is degraded; run `cavema

Error message

${profile.display_name} integration is degraded; run `caveman doctor ${agent}` before changing it

What it means

enable refuses to mutate an integration that nativeIntegrationStatus() reports as installed but degraded - a journal exists, yet pack version, owned blocks, the route, the proxy capability (native_runtime_v1), recovery MCP, or a pending transaction no longer match. Mutating a degraded install without repair could orphan the existing journal, so the command points at `caveman doctor <agent>` first.

Source

Thrown at packages/cli/src/index.ts:7959

  const profiles = detected
    ? AGENTS.filter((agent) => (agent.id === "claude" || agent.id === "codex" || agent.id === "hermes" || agent.id === "gemini" || agent.id === "opencode" || agent.id === "pi" || agent.id === "aider") && which(binOf(agent)))
    : AGENTS.filter((agent) => agent.id === target && (agent.id === "claude" || agent.id === "codex" || agent.id === "hermes" || agent.id === "gemini" || agent.id === "opencode" || agent.id === "pi" || agent.id === "aider"));
  if (profiles.length === 0) {
    console.error(detected ? "no supported native agent detected on PATH" : `caveman enable: supported agents are claude, codex, hermes, gemini, opencode, pi, and aider (got ${target ?? ""})`);
    process.exit(1);
  }
  const gw = gatewayURL();
  for (const profile of profiles) {
    const agent = profile.id as NativeAgent;
    const outcome = withIntegrationLock(agent, () => {
      recoverPendingNativeInstallUnlocked(agent);
      if (!which(binOf(profile))) throw new Error(`${profile.display_name} not found on PATH`);
      const mcpBinary = agent === "aider" ? undefined : nativeMcpBinaryRequired();
      nativeProxyBinaryRequired(gw);
      const existing = nativeIntegrationStatus(agent);
      if (existing.installed) {
        if (existing.state === "installed") return "already" as const;
        throw new Error(`${profile.display_name} integration is degraded; run \`caveman doctor ${agent}\` before changing it`);
      }
      const mutations = nativeMutationsFor(agent, gw, mcpBinary);
      const route = mutations.find((item) => typeof item.owned?.route === "string")?.owned?.route;
      process.stderr.write(`caveman enable ${agent}: planned user-scoped writes\n`);
      for (const mutation of mutations) process.stderr.write(`  ${mutation.kind}: ${mutation.file}\n`);
      if (typeof route === "string") process.stderr.write(`  routing: ${route}\n`);
      process.stderr.write(agent === "aider" ? "  recovery MCP: unavailable in Aider\n" : `  recovery MCP: ${mcpBinary}\n`);
      process.stderr.write(agent === "aider"
        ? `  Core: read-only ${aiderCorePath()}; lifecycle/tool interception unavailable; Ledger observational\n`
        : agent === "pi"
          ? `  lifecycle/Core/tool rewrite: bundled Pi extension -> ${nativeHookCommand(agent)}\n`
          : `  lifecycle/Core/tool rewrite: ${nativeHookCommand(agent)}${agent === "hermes" ? " via native plugin" : ` + ${cavemanBinForHook()} shrink-hook`}\n`);
      applyNativeMutations(agent, profile, mutations);
      return "enabled" as const;
    });
    if (outcome === "already") {
      process.stderr.write(`${mark("ok")} ${profile.display_name}: ${agent === "aider" ? "shallow" : "native"} Caveman already enabled\n`);
      continue;

View on GitHub (pinned to 5184b3d11a)

Solutions

  1. Run `caveman doctor <agent>` to see which component is degraded (routing / hooks / core / recovery / shared runtime).
  2. Run `caveman doctor <agent> --fix` - it recovers pending transactions, repairs drifted installs, or re-enables as needed.
  3. If the gateway moved on purpose, keep the new URL and let doctor --fix rewrite the route to match it.
  4. Only after doctor reports state 'installed' (or 'available' after disable) should enable/disable changes proceed.

Example fix

// before
$ caveman enable codex
Error: Codex integration is degraded; run `caveman doctor codex` before changing it

// after
$ caveman doctor codex --fix
$ caveman enable codex   # now reports 'already' or proceeds cleanly
Defensive patterns

Strategy: validation

Validate before calling

import { spawnSync } from 'node:child_process';
const agent = 'codex';
const r = spawnSync('caveman', ['doctor', agent], { encoding: 'utf8' });
// doctor exits non-zero when state is degraded or unavailable
if (r.status !== 0) {
  console.error(`${agent} integration not healthy; run: caveman doctor ${agent} --fix`);
  process.exit(1);
}

Prevention

When it happens

Trigger: `caveman enable <agent>` while the agent is already enabled and: caveman was upgraded (pack_version mismatch), the gateway URL changed (owned route no longer equals appendUrlPath(gatewayURL(), ...)), the proxy is old or missing the native_runtime_v1 capability, the caveman-mcp binary is gone, or .pending-<agent>.json exists (transaction_pending forces degraded).

Common situations: Re-running setup scripts after an upgrade; switching CAVE_GATEWAY_URL or ports; moving between local proxy and managed gateway; a previously interrupted enable.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@5184b3d11a (2026-08-18). Data as JSON: /api/errors/835dc18d2e1bf9c4. Report an issue: GitHub.