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

  1. If overwrite is acceptable, drop COPYFILE_EXCL - the default overwrites silently
  2. Pre-check fs.existsSync(dest) (or lstatSync) and skip or rename per your semantics
  3. 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

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)