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
- Run `caveman doctor <agent>` to see which component is degraded (routing / hooks / core / recovery / shared runtime).
- Run `caveman doctor <agent> --fix` - it recovers pending transactions, repairs drifted installs, or re-enables as needed.
- If the gateway moved on purpose, keep the new URL and let doctor --fix rewrite the route to match it.
- 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
- In setup scripts, replace bare `caveman enable <agent>` with `caveman doctor <agent> --fix` - it is idempotent and repairs degraded installs
- After changing gateway URLs or upgrading caveman, run doctor before further enable/disable calls
- Watch `caveman status` output for degraded integrations instead of re-running enable
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
- ${corePath} exists and is not exact Caveman-owned content; r
- existing Hermes Caveman native block is unjournaled; run `ca
- Hermes model provider/base_url keys are duplicated; refusing
- Hermes plugins.enabled uses inline YAML; refusing unsafe nat
- ${path} already exists; refusing to overwrite an unjournaled
AI-assisted analysis of JuliusBrussee/caveman@5184b3d11a (2026-08-18).
Data as JSON: /api/errors/835dc18d2e1bf9c4.
Report an issue: GitHub.