JuliusBrussee/caveman · error · Error

not found on PATH

Error message

${profile.display_name} not found on PATH

What it means

The preflight for agent-native setup requires the agent's executable (claude or codex) to be resolvable on PATH. If which(binOf(profile)) returns null, the agent binary cannot be found and setup cannot proceed, so it throws naming the agent's display name.

Solutions

  1. Install the missing agent CLI (e.g. `npm install -g @anthropic-ai/claude-code` or codex)
  2. Add the agent binary's directory to PATH in the shell running the setup command
  3. Verify with `which claude` / `which codex` and fix the shell profile accordingly

Example fix

// before
throw new Error(`${profile.display_name} not found on PATH`);
// after
// fix environment before running:
// export PATH="$PATH:/usr/local/bin" && caveman setup claude
Defensive patterns

Strategy: fallback

Validate before calling

import { which } from './which-or-shim';
if (!which('claude')) console.error('claude not on PATH; install it or fix PATH before running setup.');
if (!which('codex')) console.error('codex not on PATH; install it or fix PATH before running setup.');

Try / catch

try {
  runSetup(agent);
} catch (e) {
  if (String(e.message).endsWith('not found on PATH')) {
    // install the agent CLI or export PATH to include its bin directory, then retry
  }
}

Prevention

When it happens

Trigger: Running `caveman setup claude` or `caveman setup codex` when the corresponding agent binary is not installed or not on the current PATH.

Common situations: Agent installed via GUI but CLI shim not on PATH; running in a container/CI without the agent installed; PATH differs between shells (e.g. nvm/volta shims missing in non-interactive shells); typo'd install.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/6af79b190dab7c08. Report an issue: GitHub.

Appendix: source

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

  const marker = readMcpServerMarker(agent, "caveman-cloud");
  const markerKnown = !marker || sameMcpCommand(marker, pending.cloud_mcp) || sameMcpCommand(marker, pending.previous_cloud_mcp);
  const hostIsInstalled = agentNativeCloudMcpMatches(agent, pending.cloud_mcp);
  const hostIsPrevious = pending.previous_cloud_mcp
    ? agentNativeCloudMcpMatches(agent, pending.previous_cloud_mcp)
    : agentNativeCloudMcpHostAbsent(agent);
  if (!markerKnown || (!hostIsInstalled && !hostIsPrevious)) {
    throw new Error(`${agent} caveman-cloud MCP changed during interrupted removal; refusing destructive recovery`);
  }
  restoreInstalledAgentNativeBundle(agent, pending);
  unlinkSync(agentNativeBundleRemovalJournalPath(agent));
  process.stderr.write(`${mark("warn")} rolled back interrupted ${agent} agent-native bundle removal before continuing\n`);
}

function preflightAgentNativeSetup(agent: "claude" | "codex"): void {
  recoverPendingAgentNativeRemoval(agent);
  recoverPendingAgentNativeBundle(agent);
  const profile = findAgent(agent)!;
  if (!which(binOf(profile))) throw new Error(`${profile.display_name} not found on PATH`);
  const mcpBinary = nativeMcpBinaryRequired();
  const gw = gatewayURL();
  nativeProxyBinaryRequired(gw);
  withIntegrationLock(agent, () => recoverPendingNativeInstallUnlocked(agent));
  const status = nativeIntegrationStatus(agent);
  if (!status.installed) nativeMutationsFor(agent, gw, mcpBinary);
}

function ensureAgentNativeIntegration(agent: "claude" | "codex"): void {
  const status = nativeIntegrationStatus(agent);
  if (!status.installed) {
    enableNative([agent]);
    return;
  }
  if (status.state !== "installed") repairNativeAgent(agent);
}

function preflightAgentNativeBundleComponents(

View on GitHub (pinned to 3ee70a1026)