JuliusBrussee/caveman · error · Error
${agent} ${serverName} MCP config target changed while acqui
Error message
${agent} ${serverName} MCP config target changed while acquiring lock; refusing mutation What it means
Lock-consistency guard in transactOwnedMcpConfig: the caller acquired a lock on one config path, but by the time the plan was built the canonical MCP config path resolves elsewhere (config location changed, e.g. via env vars or symlink changes while the lock was held). Mutating the new path under a lock for the old path would be unsafe.
Source
Thrown at packages/cli/src/index.ts:12509
}
removePendingCopies();
process.stderr.write(`${mark("warn")} rolled back interrupted ${agent} ${serverName} MCP transaction\n`);
return "rolled-back";
}
function transactOwnedMcpConfig(
agent: "kilo" | "qwen",
serverName: string,
action: "install" | "uninstall",
mcp?: { command: string; args: string[] },
lockedConfigPath?: string,
): boolean {
const plan = action === "install"
? agent === "kilo" ? planMcpKiloJson(mcp!, serverName) : planMcpQwenJson(mcp!, serverName)
: agent === "kilo" ? planRemoveMcpKiloJson(serverName) : planRemoveMcpQwenJson(serverName);
if (!plan) return false;
if (lockedConfigPath && plan.path !== canonicalMcpConfigPath(lockedConfigPath)) {
throw new Error(`${agent} ${serverName} MCP config target changed while acquiring lock; refusing mutation`);
}
const markerPath = canonicalOwnedMcpMarkerPath(agent, serverName);
const markerBefore = fileBytes(markerPath);
if (markerBefore !== null && !validMcpMarkerBytes(markerBefore, agent, serverName)) {
throw new Error(`${markerPath} is not a valid Caveman ownership journal; refusing overwrite`);
}
const markerBeforeMode = markerBefore === null ? 0o600 : statSync(markerPath).mode & 0o777;
if (!optionalBytesEqual(fileBytes(markerPath), markerBefore)) {
throw new Error(`${markerPath} changed while planning MCP update; refusing overwrite`);
}
const markerAfter = action === "install" ? mcpMarkerBytes(mcp!, mcpServerToolName(serverName)!, plan.path) : null;
if (!plan.changed && optionalBytesEqual(markerBefore, markerAfter)) return true;
const journal: OwnedMcpPendingJournal = {
schema_version: 1,
transaction_id: randomUUID(),
agent,View on GitHub (pinned to 5184b3d11a)
Solutions
- Re-run the operation so the lock is acquired on the current canonical config path
- Avoid changing KILO_CONFIG_DIR / KILO_CONFIG or relevant symlinks while an MCP transaction is running
- Close competing Caveman instances that may have shifted the config location between lock and plan
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at packages/cli/src/index.ts:12509 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of JuliusBrussee/caveman@5184b3d11a (2026-09-06).
Data as JSON: /api/errors/e4742aaea618b300.
Report an issue: GitHub.