{"record":{"id":"e4bb4f9e444c938c","repo":"mastra-ai/mastra","slug":"isdirectoryerror-path","errorCode":null,"errorMessage":"IsDirectoryError: ${path}","messagePattern":"IsDirectoryError: (.+?)","errorType":"error_code","errorClass":"IsDirectoryError","httpStatus":null,"severity":"error","filePath":"mastracode/sdk/src/agents/sandbox-filesystem.ts","lineNumber":255,"sourceCode":"  private async execOk(script: string, context: string): Promise<SandboxCommandResult> {\n    const result = await this.exec(script);\n    if (result.exitCode !== 0) {\n      throw new Error(`${context} failed (exit ${result.exitCode}): ${result.stderr.trim() || result.stdout.trim()}`);\n    }\n    return result;\n  }\n\n  // ── File operations ────────────────────────────────────────────────────\n\n  async readFile(path: string, options?: ReadOptions): Promise<string | Buffer> {\n    const abs = await this.resolveAsync(path);\n    await this.assertContainedRealpath(abs, path);\n    // Guard clauses first: redirecting from a directory \"succeeds\" with empty\n    // output on some shells, so classify before reading.\n    const result = await this.exec(\n      `if [ -d ${shellQuote(abs)} ]; then exit ${EXIT_IS_DIRECTORY}; elif [ ! -e ${shellQuote(abs)} ]; then exit ${EXIT_NOT_FOUND}; fi; base64 < ${shellQuote(abs)}`,\n    );\n    if (result.exitCode === EXIT_IS_DIRECTORY) throw new IsDirectoryError(path);\n    if (result.exitCode === EXIT_NOT_FOUND) throw new FileNotFoundError(path);\n    if (result.exitCode !== 0) {\n      throw new Error(`readFile ${path} failed (exit ${result.exitCode}): ${result.stderr.trim()}`);\n    }\n    const buffer = Buffer.from(result.stdout.replace(/\\s/g, ''), 'base64');\n    if (options?.encoding) {\n      return buffer.toString(options.encoding);\n    }\n    return buffer;\n  }\n\n  async writeFile(path: string, content: FileContent, options?: WriteOptions): Promise<void> {\n    const abs = await this.resolveAsync(path);\n    await this.assertContainedDest(abs, path);\n    const b64 = toBuffer(content).toString('base64');\n    const dir = posixPath.dirname(abs);\n    const mkdir = options?.recursive === false ? '' : `mkdir -p ${shellQuote(dir)} && `;\n    if (options?.overwrite === false) {","sourceCodeStart":237,"sourceCodeEnd":273,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/sdk/src/agents/sandbox-filesystem.ts#L237-L273","documentation":"readFile throws a typed IsDirectoryError when the requested path is a directory rather than a file. The sandbox classifies this explicitly (before attempting a read) because reading a directory through redirection can silently 'succeed' with empty output on some shells, which would return a misleading empty file. Catching this typed error lets callers branch to a directory-listing API instead.","triggerScenarios":"Calling readFile (the public result() flow) with a path that exists in the sandbox and passes containment, but is a directory — e.g. readFile('src') where 'src' is a folder; the guard script exits with the reserved IS_DIRECTORY code and readFile converts it into this error.","commonSituations":"Path built by joining segments where the final segment is actually a directory; agent/tool output suggesting a folder path to a file-reading API; code that lists a parent directory and passes entries back without filtering by type; case/extension confusion that collides with a directory name.","solutions":["Catch IsDirectoryError and call the sandbox directory-listing API instead of readFile","Validate the target is a file before reading (e.g. list the parent directory and check the entry type)","Correct the path so it names the actual file (add the filename after the directory segment)"],"exampleFix":"// before\nconst content = await fs.readFile('src'); // src is a folder → IsDirectoryError\n// after\ntry {\n  const content = await fs.readFile('src/index.ts');\n} catch (e) {\n  if (e instanceof IsDirectoryError) {\n    const entries = await fs.listDir('src');\n  }\n}","handlingStrategy":"try-catch","validationCode":"// Check entry type before reading by listing the parent directory\nconst dir = parentOf(target);\nconst name = baseName(target);\nconst entries = await fs.listDir(dir);\nif (!entries.some(e => e.name === name && e.type === 'file')) {\n  throw new Error(`${target} is not a file`);\n}","typeGuard":"import { IsDirectoryError } from './sandbox-filesystem';\nfunction isDirectoryError(e: unknown): e IsDirectoryError {\n  return e instanceof IsDirectoryError;\n}","tryCatchPattern":"try {\n  return await fs.readFile(p);\n} catch (err) {\n  if (err instanceof IsDirectoryError) {\n    return listDirectoryInstead(p); // branch to directory listing\n  }\n  if (err instanceof FileNotFoundError) {\n    return null;\n  }\n  throw err;\n}","preventionTips":["Catch the typed IsDirectoryError/FileNotFoundError classes rather than matching message text","Filter directory listings to files before feeding paths into readFile","When building paths from tool/agent output, validate the final segment names a file","Resolve ambiguous paths (path without filename) by listing the directory first"],"tags":["filesystem","read-file","directory","typed-error"],"backgroundTag":"is-directory-error","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}