{"record":{"id":"f966b85e90aee82c","repo":"remix-run/remix","slug":"could-not-determine-an-app-name-from-the-target-di","errorCode":null,"errorMessage":"Could not determine an app name from the target directory.","messagePattern":"Could not determine an app name from the target directory\\.","errorType":"exception","errorClass":"UsageError","httpStatus":null,"severity":"error","filePath":"packages/cli/src/lib/bootstrap-project.ts","lineNumber":57,"sourceCode":"export type BootstrapProgressReporter = StepProgressReporter<BootstrapProjectPhase>\n\ninterface BootstrapConfig {\n  appDisplayName: string\n  packageName: string\n  remixVersion: string\n}\n\ntype TemplateValues = Record<string, string>\n\nexport async function bootstrapProject(\n  options: BootstrapProjectOptions,\n  progress?: BootstrapProgressReporter,\n): Promise<BootstrappedProject> {\n  let cwd = options.cwd ?? process.cwd()\n  let targetDir = path.resolve(cwd, options.targetDir)\n  let rawAppName = options.appName ?? path.basename(targetDir)\n  if (rawAppName.length === 0) {\n    throw appNameUnavailable(targetDir)\n  }\n\n  let config = {\n    appDisplayName: options.appName ?? humanizeName(rawAppName),\n    packageName: toPackageName(rawAppName),\n    remixVersion: readDefaultRemixVersion(options.remixVersion),\n  } satisfies BootstrapConfig\n\n  await runProgressStep(progress, 'prepare-target-directory', () =>\n    ensureTargetDirectory(targetDir, options.force),\n  )\n  await runProgressStep(progress, 'generate-scaffold-files', async () =>\n    copyTemplateDirectory({\n      sourceDir: await resolveTemplateDirectory(),\n      targetDir,\n      templateValues: createTemplateValues(config),\n    }),\n  )","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/cli/src/lib/bootstrap-project.ts#L39-L75","documentation":"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.","triggerScenarios":"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.","commonSituations":"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/'.","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"],"exampleFix":"// before\nawait bootstrapProject({ targetDir: path.resolve(cwd, input + '/') })\n\n// after\nawait bootstrapProject({ targetDir: path.resolve(cwd, input), appName: input })","handlingStrategy":"validation","validationCode":"function validTargetDir(dir: string): boolean {\n  let resolved = path.resolve(dir)\n  return path.basename(resolved).length > 0\n}","typeGuard":"function isBootstrappableTarget(dir: string): dir is string {\n  return path.basename(path.resolve(dir)).length > 0\n}","tryCatchPattern":"try {\n  await bootstrapProject(options)\n} catch (error) {\n  if (/app name from the target directory/.test(error.message)) {\n    options.appName = fallbackName\n    return bootstrapProject(options)\n  }\n  throw error\n}","preventionTips":["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"],"tags":["cli","scaffolding","filesystem"],"backgroundTag":"invalid-cli-argument","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}