remix-run/react-router · error · CopyTemplateError

"${color.bold(template)}" is an invalid template. Run ${colo

Error message

"${color.bold(template)}" is an invalid template. Run ${color.bold("create-react-router --help")} to see supported template formats.

What it means

Thrown by copyTemplate() when the --template value matches none of the supported formats, checked in order: an existing local path or file:// URL, a GitHub owner/repo shorthand (optionally with nested directories), a valid https://github.com repo URL (hostname must be exactly github.com, extra path segments must start with /tree/), or any other URL. Anything that is not resolvable on disk and not parseable as a URL falls through to this error.

Source

Thrown at packages/create-react-router/copy-template.ts:56

    if (isGithubRepoShorthand(template)) {
      log(`Using the template from the "${template}" repo`);
      await copyTemplateFromGithubRepoShorthand(template, destPath, options);
      return;
    }

    if (isValidGithubRepoUrl(template)) {
      log(`Using the template from "${template}"`);
      await copyTemplateFromGithubRepoUrl(template, destPath, options);
      return;
    }

    if (isUrl(template)) {
      log(`Using the template from "${template}"`);
      await copyTemplateFromGenericUrl(template, destPath, options);
      return;
    }

    throw new CopyTemplateError(
      `"${color.bold(template)}" is an invalid template. Run ${color.bold(
        "create-react-router --help",
      )} to see supported template formats.`,
    );
  } catch (error) {
    await options.onError(error);
  }
}

interface CopyTemplateOptions {
  debug?: boolean;
  token?: string;
  onError(error: unknown): any;
  log?(message: string): any;
}

function isLocalFilePath(input: string): boolean {
  try {

View on GitHub (pinned to 6beaca3952)

Solutions

  1. Run create-react-router --help and copy one of the listed example formats exactly
  2. For GitHub URLs use https://github.com/:owner/:repo or https://github.com/:owner/:repo/tree/:branch/:directory (no www, https only)
  3. For shorthand use :owner/:repo or :owner/:repo/:directory with no scheme prefix
  4. For local templates, verify the path exists from your current directory, or use an absolute path or file:// URL

Example fix

# before
npx create-react-router my-app --template www.github.com/acme/template

# after
npx create-react-router my-app --template https://github.com/acme/template
Defensive patterns

Strategy: validation

Validate before calling

import fs from "node:fs";
import path from "node:path";

function validateTemplate(t: string): "local" | "shorthand" | "url" {
  if (t.startsWith("file://") || fs.existsSync(path.resolve(t))) return "local";
  if (!/^https?:/.test(t) && /^[\w-]+\/[\w-.]+(\/[\w-.]+)*$/.test(t)) return "shorthand";
  try {
    const u = new URL(t);
    if (u.protocol !== "https:") throw new Error("only https:// URLs are supported");
    if (u.hostname === "github.com" && u.pathname.split("/").length > 3
        && u.pathname.split("/")[3] !== "tree") {
      throw new Error("GitHub URLs with extra segments must use /tree/:branch");
    }
    return "url";
  } catch {
    throw new Error(`Invalid --template value: ${t}`);
  }
}

Type guard

function isValidTemplateInput(t: string): boolean {
  try { validateTemplate(t); return true; } catch { return false; }
}

Try / catch

import { copyTemplate, CopyTemplateError } from "create-react-router/copy-template";

try {
  await copyTemplate(template, dest, { onError: (e) => { throw e; } });
} catch (e) {
  if (e instanceof CopyTemplateError && e.message.includes("is an invalid template")) {
    console.error("Unsupported template format. Run `create-react-router --help` for valid forms.");
    process.exit(1);
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing --template with a typo'd shorthand like 'remix-run/react-router-templat'; a URL with a non-https scheme (git+ssh://, ssh://, http://); 'https://www.github.com/...' (only hostname 'github.com' passes isValidGithubRepoUrl); a shorthand with invalid characters; or a relative path that does not exist on disk, so existsSync() fails and it is not treated as local.

Common situations: Copying a GitHub URL from the browser that includes 'www' or uses '/blob/' instead of '/tree/'; using SSH-style git URLs; referencing a local template folder from the wrong working directory; simple typos in repo names.

Understand the failure class

Background: "Unknown argument", "Invalid value", and "must be one of": invalid CLI argument errors explained — this error's family across 35 libraries.

Related errors


AI-assisted analysis of remix-run/react-router@6beaca3952 (2026-08-18). Data as JSON: /api/errors/5a58347a44d169b8. Report an issue: GitHub.