{"record":{"id":"709bc2548affa513","repo":"remix-run/react-router","slug":"template-is-an-invalid-template-run-create","errorCode":null,"errorMessage":"\"${template}\" is an invalid template. Run \"create-react-router --help\" to see supported template formats.","messagePattern":"\"(.+?)\" is an invalid template\\. Run \"create-react-router --help\" to see supported template formats\\.","errorType":"exception","errorClass":"CopyTemplateError","httpStatus":null,"severity":"error","filePath":"packages/create-react-router/copy-template.ts","lineNumber":56,"sourceCode":"    if (isGithubRepoShorthand(template)) {\n      log(`Using the template from the \"${template}\" repo`);\n      await copyTemplateFromGithubRepoShorthand(template, destPath, options);\n      return;\n    }\n\n    if (isValidGithubRepoUrl(template)) {\n      log(`Using the template from \"${template}\"`);\n      await copyTemplateFromGithubRepoUrl(template, destPath, options);\n      return;\n    }\n\n    if (isUrl(template)) {\n      log(`Using the template from \"${template}\"`);\n      await copyTemplateFromGenericUrl(template, destPath, options);\n      return;\n    }\n\n    throw new CopyTemplateError(\n      `\"${color.bold(template)}\" is an invalid template. Run ${color.bold(\n        \"create-react-router --help\",\n      )} to see supported template formats.`,\n    );\n  } catch (error) {\n    await options.onError(error);\n  }\n}\n\ninterface CopyTemplateOptions {\n  debug?: boolean;\n  token?: string;\n  onError(error: unknown): any;\n  log?(message: string): any;\n}\n\nfunction isLocalFilePath(input: string): boolean {\n  try {","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/remix-run/react-router/blob/1fd704a7dabcbe3ae09d7387b460e6acaba30ec1/packages/create-react-router/copy-template.ts#L38-L74","documentation":"Thrown by copyTemplate() when the --template value matches none of the recognized formats: a local file path (file:// or existing path), GitHub owner/repo shorthand, a full github.com URL, or any other http(s) URL. The string is passed through isLocalFilePath, isGithubRepoShorthand, isValidGithubRepoUrl, and isUrl in order; if all return false, the value is rejected. The error message echoes the offending template and points to create-react-router --help.","triggerScenarios":"Running create-react-router --template <x> where <x> is neither an existing path, nor a owner/repo shorthand (regex ^[\\w-]+/[\\w-.]+(/...)*$), nor an https://github.com/... URL, nor any other valid URL (isUrl uses URL parsing). Examples: a typo like 'remix-run/react-router-extra' with a disallowed char, an ssh git@ URL, an 'ftp://' URL, or a relative path that doesn't exist on disk.","commonSituations":"Passing an SSH clone URL (git@github.com:owner/repo.git) instead of HTTPS; passing a path that does not exist yet so isLocalFilePath's fs.existsSync returns false; using a GitLab/Bitbucket URL which is not a generic tarball URL; trailing slash or fragment that breaks the GitHub URL validator; copy-pasting a template name with a typo.","solutions":["Run create-react-router --help and copy a supported template format verbatim (owner/repo, https://github.com/owner/repo, https://host/file.tar.gz, ./local/dir).","If using a local path, verify it exists: ls <path> before passing --template; fix the path or use an absolute path.","For GitHub repos, use the HTTPS URL form https://github.com/:owner/:repo (optionally /tree/:branch/:dir) rather than ssh/git@ URLs.","For non-GitHub hosts, point --template at a direct .tar.gz URL rather than the repo page.","Omit --template entirely to use the default template (remix-run/react-router-templates default)."],"exampleFix":"// before\ncreate-react-router my-app --template git@github.com:remix-run/react-router.git\n// after\ncreate-react-router my-app --template https://github.com/remix-run/react-router","handlingStrategy":"validation","validationCode":"// Validate a --template value before invoking the CLI programmatically\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nfunction isUrl(s: string): boolean { try { new URL(s); return true; } catch { return false; } }\nfunction isLocalPath(s: string): boolean {\n  if (s.startsWith('file://')) return true;\n  try { return fs.existsSync(path.isAbsolute(s) ? s : path.resolve(process.cwd(), s)); } catch { return false; }\n}\nfunction isGithubShorthand(s: string): boolean { return !isUrl(s) && /^[\\w-]+\\/[\\w-.]+(\\/[\\w-.]+)*$/.test(s); }\nfunction isValidGithubUrl(s: string): boolean {\n  if (!isUrl(s)) return false;\n  try { const u = new URL(s); const seg = u.pathname.slice(1).split('/');\n    return u.protocol === 'https:' && u.hostname === 'github.com' && seg.length >= 2 && (seg.length > 2 ? seg[2] === 'tree' && seg.length >= 4 : true);\n  } catch { return false; }\n}\nexport function isValidTemplate(t: string): boolean {\n  return isLocalPath(t) || isGithubShorthand(t) || isValidGithubUrl(t) || isUrl(t);\n}\n// if (!isValidTemplate(template)) fail fast with a friendlier message","typeGuard":"function isValidTemplate(t: unknown): t is string {\n  if (typeof t !== 'string') return false;\n  return isLocalPath(t) || isGithubShorthand(t) || isValidGithubUrl(t) || isUrl(t);\n}","tryCatchPattern":null,"preventionTips":["Always copy template formats from create-react-router --help rather than typing from memory.","For local paths, run ls <path> immediately before the CLI invocation.","Prefer https://github.com/:owner/:repo URLs over ssh/git@ clone URLs.","In CI, default --template to a known-good URL or omit it to use the official default."],"tags":["cli","template","validation","create-react-router"],"backgroundTag":null,"analyzedSha":"1fd704a7dabcbe3ae09d7387b460e6acaba30ec1","analyzedAt":"2026-08-12T13:54:57.804Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}