strapi/strapi · error
Invalid GitHub template URL: ${template}
Error message
Invalid GitHub template URL: ${template} What it means
Thrown by copyTemplate() when a GitHub URL template's pathname has a 3rd segment that is neither undefined nor 'tree'. Strapi parses GitHub URLs as /owner/repo/tree/branch/path; any other 3rd segment (e.g. 'blob', 'commit') is rejected as an unsupported template URL shape.
Source
Thrown at packages/cli/create-strapi-app/src/utils/template.ts:74
retries: 3,
onRetry(err, attempt) {
console.log(`Retrying to download the template. Attempt ${attempt}. Error: ${err}`);
},
}
);
return;
}
if (isGithubRepo(template)) {
const url = new URL(template);
const [owner, repo, t, branch, ...pathSegments] = stripTrailingSlash(
url.pathname.slice(1)
).split('/');
if (t !== undefined && t !== 'tree') {
throw new Error(`Invalid GitHub template URL: ${template}`);
}
if (scope.templateBranch) {
await retry(
() =>
downloadGithubRepo(rootPath, {
owner,
repo,
branch: scope.templateBranch,
subPath: scope.templatePath,
}),
{
retries: 3,
onRetry(err, attempt) {
console.log(`Retrying to download the template. Attempt ${attempt}. Error: ${err}`);
},
}
);View on GitHub (pinned to 4a4101264d)
Solutions
- Use a URL copied from the repo's branch/tree view, or omit the path and use `owner/repo` shorthand.
- Replace `/blob/` with `/tree/` in the URL.
- Use the shorthand `owner/repo[/subpath]` form instead of a full URL.
- Pin the branch via `--template-branch` and pass a simpler template URL.
Example fix
// before npx create-strapi-app my-app --template https://github.com/foo/bar/blob/main/template // after npx create-strapi-app my-app --template https://github.com/foo/bar/tree/main/template
Defensive patterns
Strategy: validation
Validate before calling
// Validate GitHub URL shape before calling copyTemplate
function isValidTemplateUrl(u: string): boolean {
try {
const url = new URL(u);
if (url.origin !== 'https://github.com') return false;
const [, , , t] = url.pathname.split('/');
return t === undefined || t === 'tree';
} catch { return false; }
} Type guard
const isTreeGithubUrl = (u: string): boolean => {
try {
const url = new URL(u);
if (url.origin !== 'https://github.com') return false;
const seg = url.pathname.split('/').filter(Boolean);
return seg.length >= 2 && (seg.length === 2 || seg[2] === 'tree');
} catch { return false; }
}; Try / catch
try {
await copyTemplate(scope, rootPath);
} catch (e) {
if (/Invalid GitHub template URL/.test(e.message)) {
console.error('Use a /tree/ GitHub URL or the owner/repo shorthand.');
} else throw e;
} Prevention
- Copy template URLs from the repo's tree view, not a file's blob view.
- Prefer the `owner/repo[/path]` shorthand to avoid URL-shape errors.
- Validate the URL shape before passing it to create-strapi-app.
When it happens
Trigger: scope.template passes isGithubRepo() (https://github.com origin) so copyTemplate runs new URL(template) and splits the pathname into [owner, repo, t, branch, ...pathSegments]; throws when t is defined and !== 'tree' (e.g. the user pasted a /blob/ URL).
Common situations: User pasted a `https://github.com/.../blob/main/...` URL copied from viewing a file instead of the repo tree; URL points to a commit, pull, or wiki path; the URL was constructed by a tool using a non-tree GitHub route.
Related errors
- Could not find a template at https://github.com/${owner}/${r
- Failed to download ${url}
- Missing template or example app option
- Error detecting ${packageManager} version: ${err}
- Environment details ${targetEnvironment} not found.
AI-assisted analysis of strapi/strapi@4a4101264d (2026-08-12).
Data as JSON: /api/errors/21c847576a349b4d.
Report an issue: GitHub.