mastra-ai/mastra · error · FileExistsError

FileExistsError: ${path}

Error message

FileExistsError: ${path}

What it means

When writeFile is called with { overwrite: false }, the sandbox performs an atomic create using `set -C` (noclobber): if the destination already exists, the redirect fails and the shell exits with EXIT_EXISTS, causing a FileExistsError(path). The library throws this instead of silently clobbering an existing file, using the redirect itself as the exclusivity check to avoid a race with concurrent writers.

Source

Thrown at mastracode/sdk/src/agents/sandbox-filesystem.ts:279

    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) {
      // `set -C` (noclobber) makes the redirect itself the exclusivity check —
      // no exists() pre-check that could race with a concurrent writer.
      const result = await this.exec(
        `${mkdir}{ (set -C; printf %s ${shellQuote(b64)} | base64 -d > ${shellQuote(abs)}) 2>/dev/null || { [ -e ${shellQuote(abs)} ] && exit ${EXIT_EXISTS} || exit 1; }; }`,
      );
      if (result.exitCode === EXIT_EXISTS) throw new FileExistsError(path);
      if (result.exitCode !== 0) {
        throw new Error(`writeFile ${path} failed (exit ${result.exitCode}): ${result.stderr.trim()}`);
      }
      return;
    }
    await this.execOk(`${mkdir}printf %s ${shellQuote(b64)} | base64 -d > ${shellQuote(abs)}`, `writeFile ${path}`);
  }

  async appendFile(path: string, content: FileContent): Promise<void> {
    const abs = await this.resolveAsync(path);
    await this.assertContainedDest(abs, path);
    const b64 = toBuffer(content).toString('base64');
    await this.execOk(
      `mkdir -p ${shellQuote(posixPath.dirname(abs))} && printf %s ${shellQuote(b64)} | base64 -d >> ${shellQuote(abs)}`,
      `appendFile ${path}`,
    );
  }

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Pass { overwrite: true } (or omit options) if replacing the file is acceptable.
  2. Delete the existing file first with deleteFile(path, { force: true }) before writing.
  3. Write to a unique filename (timestamp/UUID suffix) to avoid collisions.
  4. Catch FileExistsError and treat it as 'already created by someone else' — often the desired outcome for lock-file patterns.

Example fix

// before\nawait fs.writeFile('state.lock', 'claimed', { overwrite: false });\n// after\ntry {\n  await fs.writeFile('state.lock', 'claimed', { overwrite: false });\n} catch (e) {\n  if (!(e instanceof FileExistsError)) throw e;\n  // lock already held — proceed or bail\n}
Defensive patterns

Strategy: try-catch

Validate before calling

// Optional pre-check (racy — noclobber in writeFile is the real guarantee)\nconst existing = await fs.readDirectory(parentDir);\nconst willConflict = existing.files.some(f => f.path === targetName);

Type guard

function isFileExistsError(e: unknown): e is FileExistsError {\n  return e instanceof FileExistsError;\n}

Try / catch

try {\n  await fs.writeFile(path, content, { overwrite: false });\n} catch (e) {\n  if (e instanceof FileExistsError) {\n    // create-only semantics: file already present, decide to reuse or bail\n  } else {\n    throw e;\n  }\n}

Prevention

When it happens

Trigger: writeFile(path, content, { overwrite: false }) when path already exists in the sandbox workspace — e.g. re-running an idempotent-looking step, two agents writing the same output path, or a leftover file from a previous run.

Common situations: Retrying a failed agent run without cleaning the workdir; intentional create-only semantics (lock files, 'first writer wins'); a concurrent workflow instance racing for the same output filename.

Related errors


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