{"record":{"id":"21c847576a349b4d","repo":"strapi/strapi","slug":"invalid-github-template-url-template","errorCode":null,"errorMessage":"Invalid GitHub template URL: ${template}","messagePattern":"Invalid GitHub template URL: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/create-strapi-app/src/utils/template.ts","lineNumber":74,"sourceCode":"        retries: 3,\n        onRetry(err, attempt) {\n          console.log(`Retrying to download the template. Attempt ${attempt}. Error: ${err}`);\n        },\n      }\n    );\n\n    return;\n  }\n\n  if (isGithubRepo(template)) {\n    const url = new URL(template);\n\n    const [owner, repo, t, branch, ...pathSegments] = stripTrailingSlash(\n      url.pathname.slice(1)\n    ).split('/');\n\n    if (t !== undefined && t !== 'tree') {\n      throw new Error(`Invalid GitHub template URL: ${template}`);\n    }\n\n    if (scope.templateBranch) {\n      await retry(\n        () =>\n          downloadGithubRepo(rootPath, {\n            owner,\n            repo,\n            branch: scope.templateBranch,\n            subPath: scope.templatePath,\n          }),\n        {\n          retries: 3,\n          onRetry(err, attempt) {\n            console.log(`Retrying to download the template. Attempt ${attempt}. Error: ${err}`);\n          },\n        }\n      );","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/strapi/strapi/blob/4a4101264d7098754df36e85fa629fd2f2349d8c/packages/cli/create-strapi-app/src/utils/template.ts#L56-L92","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nnpx create-strapi-app my-app --template https://github.com/foo/bar/blob/main/template\n// after\nnpx create-strapi-app my-app --template https://github.com/foo/bar/tree/main/template","handlingStrategy":"validation","validationCode":"// Validate GitHub URL shape before calling copyTemplate\nfunction isValidTemplateUrl(u: string): boolean {\n  try {\n    const url = new URL(u);\n    if (url.origin !== 'https://github.com') return false;\n    const [, , , t] = url.pathname.split('/');\n    return t === undefined || t === 'tree';\n  } catch { return false; }\n}","typeGuard":"const isTreeGithubUrl = (u: string): boolean => {\n  try {\n    const url = new URL(u);\n    if (url.origin !== 'https://github.com') return false;\n    const seg = url.pathname.split('/').filter(Boolean);\n    return seg.length >= 2 && (seg.length === 2 || seg[2] === 'tree');\n  } catch { return false; }\n};","tryCatchPattern":"try {\n  await copyTemplate(scope, rootPath);\n} catch (e) {\n  if (/Invalid GitHub template URL/.test(e.message)) {\n    console.error('Use a /tree/ GitHub URL or the owner/repo shorthand.');\n  } else throw e;\n}","preventionTips":["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."],"tags":["create-strapi-app","template","github","url-validation","cli"],"backgroundTag":null,"analyzedSha":"4a4101264d7098754df36e85fa629fd2f2349d8c","analyzedAt":"2026-08-12T12:47:50.760Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}