remix-run/remix · warning · UsageError
Target directory is not empty: ${targetDir}. Re-run with --f
Error message
Target directory is not empty: ${targetDir}. Re-run with --force to continue. What it means
The target directory exists and already contains entries, and --force was not passed, so bootstrap aborts to avoid overwriting existing content. Re-running with --force explicitly opts into writing into the non-empty directory.
Source
Thrown at packages/cli/src/lib/bootstrap-project.ts:174
}
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,
targetDir,
templateValues,
}: {
sourceDir: string
targetDir: stringView on GitHub (pinned to 9696913134)
Solutions
- Re-run with --force to scaffold into the existing directory
- Scaffold into a fresh empty directory instead
- Clean the target directory (move or delete existing files) first
Example fix
# before $ remix new . # after $ remix new . --force
Defensive patterns
Strategy: validation
Validate before calling
import { readdirSync, existsSync, statSync } from 'node:fs'
function targetIsEmpty(dir: string): boolean {
if (!existsSync(dir)) return true
if (!statSync(dir).isDirectory()) return false
return readdirSync(dir).length === 0
} Try / catch
try {
await bootstrapProject({ ...opts, force: opts.force })
} catch (error) {
if (/not empty/i.test(error.message) && opts.allowOverwrite) {
return bootstrapProject({ ...opts, force: true })
}
throw error
} Prevention
- Scaffold into a brand-new directory instead of '.' when possible
- Only pass --force after confirming existing files are disposable
- Remember hidden dotfiles count as entries and trip the guard
When it happens
Trigger: ensureTargetDirectory finds entries.length > 0 in the existing targetDir and the force flag is false; any create/scaffold command pointed at a populated directory without --force.
Common situations: Running remix new . inside a repo that already has files (README, .git); re-running scaffolding after a partial failed attempt; pointing at a shared folder with dotfiles (note: any entry counts, including hidden ones).
Related errors
- Could not determine an app name from the target directory.
- Target path is not a directory: ${targetDir}
- 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/2d5a4bd6451d0c9a.
Report an issue: GitHub.