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
- Pass { overwrite: true } (or omit options) if replacing the file is acceptable.
- Delete the existing file first with deleteFile(path, { force: true }) before writing.
- Write to a unique filename (timestamp/UUID suffix) to avoid collisions.
- 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
- Use overwrite:false only when create-once semantics are intended (locks, idempotency markers).
- Clean the workdir between runs, or write to unique names.
- Treat FileExistsError as an expected outcome for first-writer-wins flows.
- Don't add your own exists() pre-check to decide overwrite — use overwrite:true instead.
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
- pull-failed
- Sandbox workspace root resolution returned an empty path
- Path escapes workspace root: ${inputPath}
- Unable to verify path stays within workspace root: ${inputPa
- ${context} failed (exit ${result.exitCode}): ${result.stderr
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/231df12cda677180.
Report an issue: GitHub.