remix-run/remix · error · UsageError

Target path is not a directory: ${targetDir}

Error message

Target path is not a directory: ${targetDir}

What it means

Project bootstrap requires the target path to be a directory, but fs.stat succeeded and reported something else (a file, socket, etc.). Scaffolding refuses to write a project into a path occupied by a non-directory.

Source

Thrown at packages/cli/src/lib/bootstrap-project.ts:169

  }
  packageJson.devDependencies = {
    ...packageJson.devDependencies,
    '@types/node': 'latest',
    typescript: 'latest',
  }
  packageJson.engines = {
    ...packageJson.engines,
    node: `>=${MINIMUM_SUPPORTED_NODE_VERSION}`,
  }

  await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n', 'utf8')
}

async function ensureTargetDirectory(targetDir: string, force: boolean): Promise<void> {
  try {
    let stats = await fs.stat(targetDir)
    if (!stats.isDirectory()) {
      throw targetPathNotDirectory(targetDir)
    }

    let entries = await fs.readdir(targetDir)
    if (entries.length > 0 && !force) {
      throw targetDirectoryNotEmpty(targetDir)
    }
  } catch (error) {
    let nodeError = error as NodeJS.ErrnoException
    if (nodeError.code !== 'ENOENT') {
      throw error
    }
  }

  await fs.mkdir(targetDir, { recursive: true })
}

async function copyTemplateDirectory({
  sourceDir,

View on GitHub (pinned to 9696913134)

Solutions

  1. Choose a different target directory name
  2. Delete or rename the existing file occupying the path
  3. If it is a symlink to a file, remove or retarget it

Example fix

# before
$ touch myapp && remix new myapp

# after
$ rm myapp && remix new myapp
Defensive patterns

Strategy: type-guard

Validate before calling

import { statSync } from 'node:fs'

function targetIsDirectory(p: string): boolean {
  try { return statSync(p).isDirectory() } catch { return true } // ENOENT is fine for scaffolding
}

Type guard

function isSafeBootstrapTarget(p: string): boolean {
  try {
    let s = statSync(p)
    return s.isDirectory() || s.isFile() === false
  } catch { return true }
}

Try / catch

try {
  await bootstrapProject(opts)
} catch (error) {
  if (/not a directory/i.test(error.message)) chooseAnotherName()
  else throw error
}

Prevention

When it happens

Trigger: ensureTargetDirectory stats the resolved targetDir and it exists but is a regular file or other non-directory entry, e.g. remix new notes where ./notes is an existing file.

Common situations: A file with the same name as the intended project directory already exists (README, lockfile, symlink to a file); scripts that pre-create a marker file at the target path.

Related errors


AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27). Data as JSON: /api/errors/78278a16710ca242. Report an issue: GitHub.