{"record":{"id":"238466dead2e0095","repo":"can1357/oh-my-pi","slug":"directory-paths-are-not-supported-by-read-fil","errorCode":null,"errorMessage":"Directory paths are not supported by read(): ${filePath}","messagePattern":"Directory paths are not supported by read\\(\\): (.+?)","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/eval/js/shared/helpers.ts","lineNumber":154,"sourceCode":"\tif (normalized.startsWith(\"..\") || normalized.includes(\"/../\") || normalized.includes(\"/..\")) {\n\t\tthrow new ToolError(`Path traversal (..) is not allowed in ${scheme}:// URLs: ${rawPath}`);\n\t}\n\tconst resolved = path.resolve(rootPath, normalized);\n\tif (resolved !== rootPath && !resolved.startsWith(`${rootPath}${path.sep}`)) {\n\t\tthrow new ToolError(`${scheme}:// path escapes its root: ${rawPath}`);\n\t}\n\treturn resolved;\n}\n\nasync function resolveRegularFile(\n\tctx: HelperContext,\n\trawPath: string,\n): Promise<{ filePath: string; file: Bun.BunFile; size: number }> {\n\tconst filePath = resolveHelperPath(ctx, rawPath, \"read\");\n\tconst file = Bun.file(filePath);\n\tconst stat = await file.stat();\n\tif (stat.isDirectory()) {\n\t\tthrow new ToolError(`Directory paths are not supported by read(): ${filePath}`);\n\t}\n\treturn { filePath, file, size: stat.size };\n}\n\nfunction getDataSize(data: string | Blob | ArrayBuffer | ArrayBufferView): number {\n\tif (typeof data === \"string\") return utf8Encoder.encode(data).byteLength;\n\tif (data instanceof Blob) return data.size;\n\tif (data instanceof ArrayBuffer) return data.byteLength;\n\treturn data.byteLength;\n}\n\nfunction isWriteData(value: unknown): value is string | Blob | ArrayBuffer | ArrayBufferView {\n\treturn (\n\t\ttypeof value === \"string\" || value instanceof Blob || value instanceof ArrayBuffer || ArrayBuffer.isView(value)\n\t);\n}\n","sourceCodeStart":136,"sourceCodeEnd":171,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/eval/js/shared/helpers.ts#L136-L171","documentation":"`resolveRegularFile` stats the resolved path with `Bun.file(...).stat()` before reading; if the target is a directory, `read()` throws this ToolError because `file.text()` cannot return directory content. Only regular files are readable through the sandbox read helper.","triggerScenarios":"Calling `read()` on a directory path — e.g. `read(\"src\")`, `read(\"local://reports/\")` — where the resolved path is an existing directory.","commonSituations":"Pointing read at a folder expecting it to list contents or concatenate files; stale assumptions after a path became a directory; trailing-slash confusion.","solutions":["Read a specific file inside the directory, e.g. `read(\"src/index.ts\")`.","If you need directory listing, use the session's file-listing tool (e.g. glob/ls) instead of the read helper.","Verify the target path exists as a file before reading (stat or a filesystem check)."],"exampleFix":"// before\nconst txt = await read(\"src\");\n// after\nconst txt = await read(\"src/index.ts\");","handlingStrategy":"validation","validationCode":"const stat = await Bun.file(path).stat();\nif (stat?.isDirectory()) throw new Error(`${path} is a directory`);","typeGuard":null,"tryCatchPattern":"try {\n\treturn await read(p);\n} catch (err) {\n\tif (String(err?.message).startsWith(\"Directory paths are not supported\")) {\n\t\treturn await read(path.join(p, \"index.ts\")); // or surface a listing tool instead\n\t}\n\tthrow err;\n}","preventionTips":["Stat or glob the target before reading when the path comes from dynamic input.","Use the listing tool for directories instead of read().","Strip trailing slashes from directory-looking inputs before constructing the read call."],"tags":["file-io","validation","sandbox","directory"],"backgroundTag":"read-on-directory","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}