remix-run/remix · error · UsageError
Could not determine an app name from the target directory.
Error message
Could not determine an app name from the target directory.
What it means
The CLI bootstrap (project scaffolding) could not derive an app name: neither an explicit appName option nor path.basename(targetDir) produced a non-empty string. This happens when the resolved target directory path ends in a separator so its basename is empty.
Source
Thrown at packages/cli/src/lib/bootstrap-project.ts:57
export type BootstrapProgressReporter = StepProgressReporter<BootstrapProjectPhase>
interface BootstrapConfig {
appDisplayName: string
packageName: string
remixVersion: string
}
type TemplateValues = Record<string, string>
export async function bootstrapProject(
options: BootstrapProjectOptions,
progress?: BootstrapProgressReporter,
): Promise<BootstrappedProject> {
let cwd = options.cwd ?? process.cwd()
let targetDir = path.resolve(cwd, options.targetDir)
let rawAppName = options.appName ?? path.basename(targetDir)
if (rawAppName.length === 0) {
throw appNameUnavailable(targetDir)
}
let config = {
appDisplayName: options.appName ?? humanizeName(rawAppName),
packageName: toPackageName(rawAppName),
remixVersion: readDefaultRemixVersion(options.remixVersion),
} satisfies BootstrapConfig
await runProgressStep(progress, 'prepare-target-directory', () =>
ensureTargetDirectory(targetDir, options.force),
)
await runProgressStep(progress, 'generate-scaffold-files', async () =>
copyTemplateDirectory({
sourceDir: await resolveTemplateDirectory(),
targetDir,
templateValues: createTemplateValues(config),
}),
)View on GitHub (pinned to 9696913134)
Solutions
- Pass an explicit app name: remix new my-app or the equivalent --app-name option
- Use a real, non-root target directory path
- Strip trailing slashes from computed targetDir values before calling bootstrap programmatically
Example fix
// before
await bootstrapProject({ targetDir: path.resolve(cwd, input + '/') })
// after
await bootstrapProject({ targetDir: path.resolve(cwd, input), appName: input }) Defensive patterns
Strategy: validation
Validate before calling
function validTargetDir(dir: string): boolean {
let resolved = path.resolve(dir)
return path.basename(resolved).length > 0
} Type guard
function isBootstrappableTarget(dir: string): dir is string {
return path.basename(path.resolve(dir)).length > 0
} Try / catch
try {
await bootstrapProject(options)
} catch (error) {
if (/app name from the target directory/.test(error.message)) {
options.appName = fallbackName
return bootstrapProject(options)
}
throw error
} Prevention
- Always pass an explicit appName in programmatic scaffolding
- Normalize and strip trailing separators from user-supplied paths
- Reject root-level paths in your wrapper before calling the CLI
When it happens
Trigger: bootstrapProject is called with a targetDir that resolves to the filesystem root (e.g. '/' or 'C:\\') or a path ending with a trailing separator such that path.basename returns '', and no options.appName is provided.
Common situations: Passing '/' or an empty/drive-root path as the target directory to a create/scaffold command; programmatic calls with a computed targetDir that accidentally normalizes to root; trailing-slash inputs like 'remix new my-app/'.
Related errors
- Target path is not a directory: ${targetDir}
- 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/f966b85e90aee82c.
Report an issue: GitHub.