{"record":{"id":"09dc660b71472d0f","repo":"JuliusBrussee/caveman","slug":"caveman-code-neither-rg-nor-grep-is-available","errorCode":null,"errorMessage":"caveman-code: neither rg nor grep is available","messagePattern":"caveman-code: neither rg nor grep is available","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/code.ts","lineNumber":286,"sourceCode":"    result: \"inline\",\n    timeoutMs: READ_TIMEOUT_MS,\n    async execute(input, signal) {\n      const root = await workspaceRoot();\n      const scope = input.path === undefined ? root : await contained(input.path);\n      const relativeScope = scope === root ? \".\" : relative(root, scope);\n      const ripgrep = [\n        \"--line-number\", \"--no-heading\", \"--color\", \"never\",\n        \"--max-count\", String(GREP_MAX_MATCHES),\n        ...(input.glob === undefined ? [] : [\"--glob\", input.glob]),\n        \"--regexp\", input.pattern, \"--\", relativeScope,\n      ];\n      let run = await runProcess(\"rg\", ripgrep, root, READ_TIMEOUT_MS, signal);\n      if (run.spawnFailed) {\n        run = await runProcess(\"grep\", [\n          \"-rnI\", \"-m\", String(GREP_MAX_MATCHES), \"-E\", \"-e\", input.pattern, \"--\", relativeScope,\n        ], root, READ_TIMEOUT_MS, signal);\n      }\n      if (run.spawnFailed) throw new Error(\"caveman-code: neither rg nor grep is available\");\n      const body = run.output.trim() === \"\" ? \"no matches\" : run.output;\n      const text = capOutput(body, caps.grep);\n      record(`grep:${input.pattern}`, text);\n      return text;\n    },\n  });\n\n  const bashTool = tool({\n    name: \"bash\",\n    description:\n      \"Run one shell command in the workspace and return its combined stdout and stderr. \" +\n      `Times out after ${BASH_TIMEOUT_MS} ms; output is capped at ${caps.bash} bytes.`,\n    input: schema.object({\n      command: schema.string(),\n      timeoutMs: schema.optional(schema.integer()),\n    }),\n    effect: \"external\",\n    result: \"inline\",","sourceCodeStart":268,"sourceCodeEnd":304,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/agent/src/code.ts#L268-L304","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before (Dockerfile)\nFROM gcr.io/distroless/nodejs\n# after\nFROM node:22-slim\nRUN apt-get update && apt-get install -y ripgrep","handlingStrategy":"fallback","validationCode":"const { execFileSync } = require(\"child_process\");\nconst hasSearchBackend = [\"rg\", \"grep\"].some((b) => {\n  try { execFileSync(b, [\"--version\"], { stdio: \"ignore\" }); return true; }\n  catch { return false; }\n});\nif (!hasSearchBackend) throw new Error(\"Install ripgrep before using the grep tool\");","typeGuard":null,"tryCatchPattern":"try {\n  await grep({ pattern, path });\n} catch (e) {\n  if (e.message.includes(\"neither rg nor grep is available\")) {\n    console.error(\"No search backend on PATH; install ripgrep or use a JS-based search fallback.\");\n  } else throw e;\n}","preventionTips":["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."],"tags":["search","missing-binary","environment"],"backgroundTag":"command-not-found","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}