JuliusBrussee/caveman · error
caveman-code: neither rg nor grep is available
Error message
caveman-code: neither rg nor grep is available
What it means
The grep tool in packages/agent/src/code.ts first tries to spawn ripgrep ('rg'); if spawning fails it falls back to 'grep -rnI -m N -E'. If both spawn attempts fail (spawnFailed on both), it throws 'caveman-code: neither rg nor grep is available', meaning no search backend exists on the host or PATH.
Solutions
- Install ripgrep on the host (apt install ripgrep / brew install ripgrep / cargo install ripgrep) — preferred backend.
- Alternatively ensure GNU/BSD grep is installed and on PATH (apt install grep, apk add grep).
- Fix the PATH seen by the process (e.g. PATH=/usr/bin:/bin:...) so existing binaries are found.
- If running in a minimal container, add rg to the image: COPY --from or the package manager in the Dockerfile.
- Verify availability: `command -v rg || command -v grep` in the same environment the agent runs in.
Example fix
# before (Dockerfile) FROM gcr.io/distroless/nodejs # after FROM node:22-slim RUN apt-get update && apt-get install -y ripgrep
Defensive patterns
Strategy: fallback
Validate before calling
const { execFileSync } = require("child_process");
const hasSearchBackend = ["rg", "grep"].some((b) => {
try { execFileSync(b, ["--version"], { stdio: "ignore" }); return true; }
catch { return false; }
});
if (!hasSearchBackend) throw new Error("Install ripgrep before using the grep tool"); Try / catch
try {
await grep({ pattern, path });
} catch (e) {
if (e.message.includes("neither rg nor grep is available")) {
console.error("No search backend on PATH; install ripgrep or use a JS-based search fallback.");
} else throw e;
} Prevention
- Install ripgrep in every runtime image used by the agent (Dockerfile: RUN apt-get install -y ripgrep).
- Verify `command -v rg || command -v grep` in the agent's environment during setup/CI.
- Do not override PATH in a way that excludes /usr/bin.
- Prefer standard base images over distroless/scratch for agent runtimes.
When it happens
Trigger: Running the grep tool on a host/container where neither rg nor grep is installed, or neither binary is on the PATH inherited by the process; a stripped-down Docker image (e.g. alpine without grep, distroless images) or a sandboxed workspace root without system tools.
Common situations: Distroless/minimal CI images; Windows hosts without grep and ripgrep not installed; custom PATH overrides in the agent environment excluding /usr/bin; containerized eval sandboxes that intentionally omit system utilities.
Related errors
- bounded_response_unavailable
- Cave Build lock exists but caveman-agent checker is…
- cave_sandbox_conformance_failed
- CAVE_SETUP_TIMEOUT must be a positive integer
- cave_transform_registry_unavailable
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/09dc660b71472d0f.
Report an issue: GitHub.
Appendix: source
Thrown at packages/agent/src/code.ts:286
result: "inline",
timeoutMs: READ_TIMEOUT_MS,
async execute(input, signal) {
const root = await workspaceRoot();
const scope = input.path === undefined ? root : await contained(input.path);
const relativeScope = scope === root ? "." : relative(root, scope);
const ripgrep = [
"--line-number", "--no-heading", "--color", "never",
"--max-count", String(GREP_MAX_MATCHES),
...(input.glob === undefined ? [] : ["--glob", input.glob]),
"--regexp", input.pattern, "--", relativeScope,
];
let run = await runProcess("rg", ripgrep, root, READ_TIMEOUT_MS, signal);
if (run.spawnFailed) {
run = await runProcess("grep", [
"-rnI", "-m", String(GREP_MAX_MATCHES), "-E", "-e", input.pattern, "--", relativeScope,
], root, READ_TIMEOUT_MS, signal);
}
if (run.spawnFailed) throw new Error("caveman-code: neither rg nor grep is available");
const body = run.output.trim() === "" ? "no matches" : run.output;
const text = capOutput(body, caps.grep);
record(`grep:${input.pattern}`, text);
return text;
},
});
const bashTool = tool({
name: "bash",
description:
"Run one shell command in the workspace and return its combined stdout and stderr. " +
`Times out after ${BASH_TIMEOUT_MS} ms; output is capped at ${caps.bash} bytes.`,
input: schema.object({
command: schema.string(),
timeoutMs: schema.optional(schema.integer()),
}),
effect: "external",
result: "inline",View on GitHub (pinned to 3ee70a1026)