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
- Choose a different target directory name
- Delete or rename the existing file occupying the path
- 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
- Check the target path is not an existing file before scaffolding
- Generate project names that cannot collide with files (fresh subdirectory names)
- In scripts, fail fast if the target exists and is not a directory
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
- Could not determine an app name from the target directory.
- Target directory is not empty: ${targetDir}. Re-run with --f
- Resolved path escapes the allowed root: ${relativePath}
- Expected a Remix config file or directory: ${fromPath}
- RMX_TARGET_DIRECTORY_NOT_EMPTY
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/78278a16710ca242.
Report an issue: GitHub.