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

  1. Pass an explicit app name: remix new my-app or the equivalent --app-name option
  2. Use a real, non-root target directory path
  3. 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

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


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