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

  1. Catch IsDirectoryError and call the sandbox directory-listing API instead of readFile
  2. Validate the target is a file before reading (e.g. list the parent directory and check the entry type)
  3. 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

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


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/e4bb4f9e444c938c. Report an issue: GitHub.