JuliusBrussee/caveman · warning · Error
caveman-code: not a file: ${input.path}
Error message
caveman-code: not a file: ${input.path} What it means
Thrown by the caveman-code `read_file` tool. The requested path was first checked for workspace containment (realpath-based), then `stat`ed; `info.isFile()` is false, meaning the path exists (or a contained symlink resolves) to a directory, FIFO, socket, device, or other non-regular file. Only regular files can be read, so the tool fails with the model-supplied path echoed back.
Source
Thrown at packages/agent/src/code.ts:242
containedPath(await workspaceRoot(), candidate);
const readFileTool = tool({
name: "read_file",
description:
"Read a UTF-8 file from the workspace. Optional offset/limit read a line range. " +
`Output is capped at ${caps.read_file} bytes.`,
input: schema.object({
path: schema.string(),
offset: schema.optional(schema.integer()),
limit: schema.optional(schema.integer()),
}),
effect: "read",
result: "inline",
timeoutMs: READ_TIMEOUT_MS,
async execute(input) {
const target = await contained(input.path);
const info = await stat(target);
if (!info.isFile()) throw new Error(`caveman-code: not a file: ${input.path}`);
const content = await readFile(target, "utf8");
const lines = content.split("\n");
const offset = Math.max(1, input.offset ?? 1);
const limit = input.limit === undefined ? lines.length : Math.max(1, input.limit);
const selected = lines.slice(offset - 1, offset - 1 + limit);
const numbered = selected
.map((line, index) => `${offset + index}\t${line}`)
.join("\n");
const text = capOutput(numbered, caps.read_file);
record(`read_file:${input.path}`, text);
return text;
},
});
const grepTool = tool({
name: "grep",
description:
"Search the workspace for a regular expression with ripgrep (grep fallback). " +View on GitHub (pinned to 27d5a3981a)
Solutions
- Instruct the model (system prompt/tool docs) to use the listing/grep tool for directories and read_file only for regular files.
- Pass an explicit file path with its extension (`src/index.ts`, not `src`).
- If a tool call must not fail the turn, catch this error in the turn loop and let the model retry with a corrected path.
Example fix
// before: model calls the tool with a directory
tools.read_file({ path: "src" });
// after
tools.read_file({ path: "src/index.ts", offset: 1, limit: 50 }); Defensive patterns
Strategy: type-guard
Validate before calling
import { stat } from "node:fs/promises";
async function isRegularFile(path: string): Promise<boolean> {
try { return (await stat(path)).isFile(); } catch { return false; }
}
// model-side guard before calling read_file: await isRegularFile(resolvedPath) Type guard
import type { Stats } from "node:fs";
function isRegularFile(info: Stats): boolean {
return info.isFile();
} Try / catch
// inside a turn loop driving the coding agent:
try {
return await tools.read_file.execute({ path });
} catch (error) {
if (error instanceof Error && error.message.startsWith("caveman-code: not a file:")) {
return `Error: ${path} is not a regular file. Use the listing or grep tool for directories.`; // let the model correct itself
}
throw error;
} Prevention
- Tool descriptions should tell the model read_file accepts regular files only.
- Offer a list/grep tool so directories are never probed with read_file.
- Treat this as a recoverable model mistake in the turn loop, not a crash.
When it happens
Trigger: A coding-agent model calls `read_file` with a directory path (e.g. `"src"` instead of `"src/index.ts"`), or with `/dev/null`, a named pipe, or a socket path inside the workspace. Containment already passed, so this is purely a not-a-regular-file rejection, not a security error.
Common situations: Models exploring an unfamiliar tree guessing file names and hitting directories; attempts to read `/proc` or `/dev` entries that happen to be symlinked into the workspace; using read_file where list/grep was intended.
Related errors
- cave_harness_aborted
- cave_vercel_terminal_failure
- cave_mastra_max_steps_invalid
- caveman agent: invalid .caveman/provider.json
- lock disappeared during validation
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/9b096beba3a286ad.
Report an issue: GitHub.