remix-run/react-router · error · Error

File collisions detected in a non-interactive environment

Error message

File collisions detected in a non-interactive environment

What it means

copyTempDirToAppDirStep() compares the template file list with the destination directory; when files collide, an interactive shell gets a confirm prompt, but a non-interactive run (no TTY, --yes implied) must abort unless --overwrite was passed. This error is that abort — the colliding file names are printed in the error output above the throw.

Solutions

  1. Scaffold into a new or empty directory
  2. Add --overwrite to intentionally replace the colliding files
  3. Remove or move the files listed in the error output, then re-run

Example fix

# before (non-interactive, dir not empty)
npx create-react-router ./existing-dir --yes

# after: explicit overwrite, or a fresh directory
npx create-react-router ./existing-dir --yes --overwrite
# or
npx create-react-router ./fresh-dir --yes
Defensive patterns

Strategy: validation

Validate before calling

import { existsSync, readdirSync } from "node:fs";

function targetIsSafe(dir: string): boolean {
  // Non-interactive runs require an empty (or absent) target unless --overwrite is passed
  return !existsSync(dir) || readdirSync(dir).length === 0;
}

const args = targetIsSafe(projectDir) ? baseArgs : [...baseArgs, "--overwrite"];

Try / catch

try {
  await createReactRouter(args);
} catch (e) {
  if ((e as Error).message === "File collisions detected in a non-interactive environment") {
    console.error("Target not empty — rerun with --overwrite or use a fresh directory");
    process.exit(1);
  }
  throw e;
}

Prevention

When it happens

Trigger: Scaffolding into a non-empty directory from CI/Docker where isInteractive() is false; re-running a scaffold step into a directory that already contains template files (README, .gitignore, package.json); no --overwrite flag included.

Common situations: Rerunning a partially failed CI scaffold; initializing into a folder that already has files; caching the target directory between CI runs.

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 remix-run/react-router@6beaca3952 (2026-08-18). Data as JSON: /api/errors/7bd67d692f2bfb5f. Report an issue: GitHub.

Appendix: source

Thrown at packages/create-react-router/index.ts:393

        lines.push(`and ${moreFiles} more...`);
      }
      return lines.join(`\n${prefix}`);
    };

    if (ctx.overwrite) {
      info(
        "Overwrite:",
        `overwriting files due to \`--overwrite\`:${getFileList("           ")}`,
      );
    } else if (!ctx.interactive) {
      error(
        "Oh no!",
        `Destination directory contains files that would be overwritten\n` +
          `         and no \`--overwrite\` flag was included in a non-interactive\n` +
          `         environment. The following files would be overwritten:` +
          getFileList("           "),
      );
      throw new Error(
        "File collisions detected in a non-interactive environment",
      );
    } else {
      if (ctx.debug) {
        debug(`Colliding files:${getFileList("          ")}`);
      }

      let { overwrite } = await ctx.prompt({
        name: "overwrite",
        type: "confirm",
        label: title("overwrite"),
        message:
          `Your project directory contains files that will be overwritten by\n` +
          `             this template (you can force with \`--overwrite\`)\n\n` +
          `             Files that would be overwritten:` +
          `${getFileList("               ")}\n\n` +
          `             Do you wish to continue?\n` +
          `             `,

View on GitHub (pinned to 6beaca3952)