{"record":{"id":"94e6eaf33cfa0756","repo":"can1357/oh-my-pi","slug":"cannot-read-directory-message","errorCode":null,"errorMessage":"Cannot read directory: ${message}","messagePattern":"Cannot read directory: (.+?)","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/read.ts","lineNumber":2333,"sourceCode":"\t\tsignal?: AbortSignal,\n\t): Promise<AgentToolResult<ReadToolDetails>> {\n\t\tconst READ_DIRECTORY_MAX_DEPTH = 2;\n\t\tconst READ_DIRECTORY_CHILD_LIMIT = 12;\n\n\t\tthrowIfAborted(signal);\n\t\tlet tree: DirectoryTree;\n\t\ttry {\n\t\t\ttree = await buildDirectoryTree(absolutePath, {\n\t\t\t\tmaxDepth: READ_DIRECTORY_MAX_DEPTH,\n\t\t\t\tperDirLimit: READ_DIRECTORY_CHILD_LIMIT,\n\t\t\t\trootLimit: null,\n\t\t\t\t// `lineCap` truncates the rendered tree itself, so apply it only when the caller\n\t\t\t\t// did not request an offset — otherwise we'd cap the first N lines before slicing.\n\t\t\t\tlineCap: offset === undefined && limit !== undefined ? limit : null,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tthrow new ToolError(`Cannot read directory: ${message}`);\n\t\t}\n\t\tthrowIfAborted(signal);\n\n\t\tconst output = tree.totalLines <= 1 ? \"(empty directory)\" : tree.rendered;\n\t\tconst details: ReadToolDetails = {\n\t\t\tisDirectory: true,\n\t\t\tresolvedPath: tree.rootPath,\n\t\t};\n\n\t\t// Slice the rendered listing when the caller passed an offset/limit. We do this\n\t\t// instead of passing the selector down to `buildDirectoryTree` because the tree\n\t\t// builder lays out entries hierarchically (per-dir caps, recent-then-elided\n\t\t// summaries); line-based slicing operates on the formatted text and matches what\n\t\t// users expect from `:N-M` on long listings.\n\t\tconst wantsSlice = offset !== undefined || limit !== undefined;\n\t\tif (wantsSlice) {\n\t\t\tconst allLines = output.split(\"\\n\");\n\t\t\tconst start = offset ? Math.max(0, offset - 1) : 0;","sourceCodeStart":2315,"sourceCodeEnd":2351,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/read.ts#L2315-L2351","documentation":"Directory reads render a tree via a tree-rendering helper; any error it raises (permission denied, IO error, symlink loops, etc.) is caught and re-thrown as a single ToolError prefixed with 'Cannot read directory:' keeping the original message.","triggerScenarios":"read() on a directory where the underlying tree walk/render throws: unreadable subdirectory (EACCES), deleted mid-scan, too many levels, or OS-level IO failure.","commonSituations":"Reading directories without execute permission on Linux, racing with an external process removing files, network mounts that time out, or very deep node_modules trees.","solutions":["Read the original message after 'Cannot read directory:' and fix the underlying cause (permissions, existence, mount).","Check permissions (chmod/chown) or read with appropriate user.","Target a specific subdirectory or file instead of the failing tree root.","Retry if the failure was a transient deletion race."],"exampleFix":"// before\nread(\"/root/secure/\")  // EACCES\n// after\nread(\"/home/user/project/\")  // accessible directory","handlingStrategy":"try-catch","validationCode":"try { fs.accessSync(dir, fs.constants.R_OK | fs.constants.X_OK); } catch { throw new Error('No read access to directory: ' + dir); }","typeGuard":"null","tryCatchPattern":"try { return await read(dir) } catch (e) { if (e instanceof ToolError && e.message.startsWith('Cannot read directory:')) { const cause = e.message.slice('Cannot read directory:'.length).trim(); /* inspect cause: EACCES, ENOENT, etc. */ } throw e; }","preventionTips":["Check read/execute permissions on directories before listing","Avoid reading directories that external processes may mutate concurrently","Prefer targeted file reads over deep tree walks of huge or networked trees"],"tags":["directory","filesystem","permissions"],"backgroundTag":"directory-read-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}