mastra-ai/mastra · error · IsDirectoryError
IsDirectoryError: ${path}
Error message
IsDirectoryError: ${path} What it means
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.
Source
Thrown at mastracode/sdk/src/agents/sandbox-filesystem.ts:255
private async execOk(script: string, context: string): Promise<SandboxCommandResult> {
const result = await this.exec(script);
if (result.exitCode !== 0) {
throw new Error(`${context} failed (exit ${result.exitCode}): ${result.stderr.trim() || result.stdout.trim()}`);
}
return result;
}
// ── File operations ────────────────────────────────────────────────────
async readFile(path: string, options?: ReadOptions): Promise<string | Buffer> {
const abs = await this.resolveAsync(path);
await this.assertContainedRealpath(abs, path);
// Guard clauses first: redirecting from a directory "succeeds" with empty
// output on some shells, so classify before reading.
const result = await this.exec(
`if [ -d ${shellQuote(abs)} ]; then exit ${EXIT_IS_DIRECTORY}; elif [ ! -e ${shellQuote(abs)} ]; then exit ${EXIT_NOT_FOUND}; fi; base64 < ${shellQuote(abs)}`,
);
if (result.exitCode === EXIT_IS_DIRECTORY) throw new IsDirectoryError(path);
if (result.exitCode === EXIT_NOT_FOUND) throw new FileNotFoundError(path);
if (result.exitCode !== 0) {
throw new Error(`readFile ${path} failed (exit ${result.exitCode}): ${result.stderr.trim()}`);
}
const buffer = Buffer.from(result.stdout.replace(/\s/g, ''), 'base64');
if (options?.encoding) {
return buffer.toString(options.encoding);
}
return buffer;
}
async writeFile(path: string, content: FileContent, options?: WriteOptions): Promise<void> {
const abs = await this.resolveAsync(path);
await this.assertContainedDest(abs, path);
const b64 = toBuffer(content).toString('base64');
const dir = posixPath.dirname(abs);
const mkdir = options?.recursive === false ? '' : `mkdir -p ${shellQuote(dir)} && `;
if (options?.overwrite === false) {View on GitHub (pinned to 75dd419e61)
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)
Example fix
// before
const content = await fs.readFile('src'); // src is a folder → IsDirectoryError
// after
try {
const content = await fs.readFile('src/index.ts');
} catch (e) {
if (e instanceof IsDirectoryError) {
const entries = await fs.listDir('src');
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// Check entry type before reading by listing the parent directory
const dir = parentOf(target);
const name = baseName(target);
const entries = await fs.listDir(dir);
if (!entries.some(e => e.name === name && e.type === 'file')) {
throw new Error(`${target} is not a file`);
} Type guard
import { IsDirectoryError } from './sandbox-filesystem';
function isDirectoryError(e: unknown): e IsDirectoryError {
return e instanceof IsDirectoryError;
} Try / catch
try {
return await fs.readFile(p);
} catch (err) {
if (err instanceof IsDirectoryError) {
return listDirectoryInstead(p); // branch to directory listing
}
if (err instanceof FileNotFoundError) {
return null;
}
throw err;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Failed to create directory: ${errorMessage}
- MASTRA_ENTRY_FILE_NOT_FOUND
- Failed to copy studio assets from "${studioSource}" to "${st
- Failed to copy studio assets from "${studioSource}" to "${st
- Failed to read studio routes manifest at "${manifestPath}":
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/e4bb4f9e444c938c.
Report an issue: GitHub.