mastra-ai/mastra · error · WorkspaceReadOnlyError

READ_ONLY

READ_ONLY

Error message

Workspace is in read-only mode. Cannot perform: ${operation}

What it means

LocalFilesystem throws WorkspaceReadOnlyError (code READ_ONLY) when any mutating filesystem operation is attempted while the provider was constructed with readOnly: true. It is raised in assertWritable(), which every write path (writeFile, appendFile, deleteFile, copyFile, moveFile, mkdir) calls before touching disk. The error guards a deliberate configuration choice, not a runtime fault, so no data was modified.

Source

Thrown at packages/core/src/workspace/filesystem/local-filesystem.ts:339

   * Uses the same resolution logic as internal file operations.
   * Returns `undefined` if the path violates containment.
   */
  resolveAbsolutePath(inputPath: string): string | undefined {
    try {
      return this.resolvePath(inputPath);
    } catch {
      // PermissionError from containment check — path is not resolvable
      return undefined;
    }
  }

  private toRelativePath(absolutePath: string): string {
    return nodePath.relative(this._basePath, absolutePath).replace(/\\/g, '/');
  }

  private assertWritable(operation: string): void {
    if (this.readOnly) {
      throw new WorkspaceReadOnlyError(operation);
    }
  }

  /**
   * Verify that the resolved path doesn't escape basePath via symlinks.
   * Uses realpath to resolve symlinks and check the actual target.
   */
  private async assertPathContained(absolutePath: string): Promise<void> {
    if (!this._contained) return;

    if (this._allowedPaths.some(root => this._isWithinRoot(absolutePath, root))) {
      return;
    }

    // Resolve symlinks for the target path. If it doesn't exist,
    // there are no symlinks to escape through — nothing to check.
    let targetReal: string;
    try {

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Remove readOnly: true from the LocalFilesystem options (or use a separate writable workspace) if writes are intended.
  2. Check workspace.filesystem.info / isReadOnly before issuing write calls and route writes to a writable provider.
  3. Catch WorkspaceReadOnlyError in the agent/tool layer and surface a clear message telling the model the workspace cannot be written.
  4. If only some mounts are read-only, target the write to a mounted filesystem that is writable.

Example fix

// before
const ws = new Workspace({ filesystem: new LocalFilesystem({ basePath: './data', readOnly: true }) });
await ws.writeFile('out.txt', 'hi'); // throws WorkspaceReadOnlyError
// after
const ws = new Workspace({ filesystem: new LocalFilesystem({ basePath: './data' }) });
await ws.writeFile('out.txt', 'hi');
Defensive patterns

Strategy: try-catch

Validate before calling

import { LocalFilesystem } from '@mastra/core/workspace';
if (fsProvider.info?.readOnly) {
  throw new Error('Workspace filesystem is read-only; writes are not permitted');
}

Type guard

import { WorkspaceReadOnlyError } from '@mastra/core/workspace/errors';
function isReadOnlyError(e: unknown): e is WorkspaceReadOnlyError {
  return e instanceof WorkspaceReadOnlyError ||
    (e instanceof Error && 'code' in e && (e as { code?: string }).code === 'READ_ONLY');
}

Try / catch

try {
  await ws.writeFile('out.txt', data);
} catch (e) {
  if (isReadOnlyError(e)) {
    logger.warn('Workspace is read-only; skipping write');
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling writeFile, appendFile, deleteFile, copyFile, moveFile, or mkdir on a LocalFilesystem created with { readOnly: true } (or a Workspace configured with a read-only filesystem/mount); agent tools that write to files when the workspace policy is read-only.

Common situations: Serving a workspace as read-only reference material (docs, skills) while an agent attempts to save output; forgetting readOnly was enabled after switching an environment to production-safe defaults; a mounted sub-filesystem is read-only while the caller expects writes to land there.

Related errors


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