denoland/deno · error · Error
A file exists at the destination
Error message
A file exists at the destination: ${destStr} What it means
Node-compat fs.copyFileSync with fs.constants.COPYFILE_EXCL first lstats the destination; if it exists it throws this Error, emulating Node's EEXIST semantics. Note that the Deno polyfill throws a plain Error whose message embeds the destination path - unlike Node it carries no code property, so detection is by catch or by pre-checking the destination yourself.
Solutions
- If overwrite is acceptable, drop COPYFILE_EXCL - the default overwrites silently
- Pre-check fs.existsSync(dest) (or lstatSync) and skip or rename per your semantics
- Wrap in try-catch and treat this message as the already-copied signal, since the polyfill error has no code field
Example fix
// before
import fs from 'node:fs';
fs.copyFileSync(src, dest, fs.constants.COPYFILE_EXCL); // throws on rerun
// after
if (!fs.existsSync(dest)) {
fs.copyFileSync(src, dest, fs.constants.COPYFILE_EXCL);
} Defensive patterns
Strategy: validation
Validate before calling
import fs from 'node:fs';
function copyExclusive(src: string, dest: string): void {
if (fs.existsSync(dest)) return; // or rename/abort per your semantics
fs.copyFileSync(src, dest, fs.constants.COPYFILE_EXCL);
} Type guard
function destinationIsFree(dest: string): boolean {
return !fs.existsSync(dest);
} Try / catch
try {
fs.copyFileSync(src, dest, fs.constants.COPYFILE_EXCL);
} catch (e) {
if (e instanceof Error && e.message.startsWith('A file exists at the destination')) {
// expected on re-run: idempotent success or explicit skip message
return;
}
throw e;
} Prevention
- Remember the Deno polyfill throws a plain Error without a code field - match on the message
- Check the destination with existsSync/lstatSync before using COPYFILE_EXCL in rerunnable scripts
- Make scripts idempotent: copy only when the destination is absent, by design rather than by exception
When it happens
Trigger: fs.copyFileSync(src, dest, fs.constants.COPYFILE_EXCL) with dest already present; passing a mode number whose low bit includes EXCL (COPYFILE_EXCL is 1); re-running an idempotent setup script that copies the same template twice.
Common situations: Deploy/bootstrap scripts copying default configs that must not clobber user edits; backup routines that intentionally refuse overwrite; CI steps re-executed against a dirty workspace.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/e24952ac0ff038db.
Report an issue: GitHub.
Appendix: source
Thrown at ext/node/polyfills/_fs/_fs_copy.ts:95
export const copyFilePromise = promisify(copyFile) as (
src: string | Buffer | URL,
dest: string | Buffer | URL,
mode?: number,
) => Promise<void>;
export function copyFileSync(
src: string | Buffer | URL,
dest: string | Buffer | URL,
mode?: number,
) {
const srcStr = getValidatedPathToString(src, "src");
const destStr = getValidatedPathToString(dest, "dest");
const modeNum = getValidMode(mode, "copyFile");
if ((modeNum & fs.COPYFILE_EXCL) === fs.COPYFILE_EXCL) {
try {
Deno.lstatSync(destStr);
throw new Error(`A file exists at the destination: ${destStr}`);
} catch (e) {
if (ObjectPrototypeIsPrototypeOf(Deno.errors.NotFound.prototype, e)) {
Deno.copyFileSync(srcStr, destStr);
} else {
throw e;
}
}
} else {
Deno.copyFileSync(srcStr, destStr);
}
}
View on GitHub (pinned to 9ad36f7a2c)