{"record":{"id":"0c9df3d8c9303573","repo":"remix-run/react-router","slug":"the-provided-template-is-not-a-valid-local-directo","errorCode":null,"errorMessage":"The provided template is not a valid local directory or tarball.","messagePattern":"The provided template is not a valid local directory or tarball\\.","errorType":"exception","errorClass":"CopyTemplateError","httpStatus":null,"severity":"error","filePath":"packages/create-react-router/copy-template.ts","lineNumber":139,"sourceCode":") {\n  await copyTemplateFromRemoteTarball(url, destPath, options);\n}\n\nasync function copyTemplateFromLocalFilePath(\n  filePath: string,\n  destPath: string,\n): Promise<boolean> {\n  if (filePath.endsWith(\".tar.gz\") || filePath.endsWith(\".tgz\")) {\n    await extractLocalTarball(filePath, destPath);\n    return false;\n  }\n  if (fs.statSync(filePath).isDirectory()) {\n    // If our template is just a directory on disk, return true here, and we'll\n    // just copy directly from there instead of \"extracting\" to a temp\n    // directory first\n    return true;\n  }\n  throw new CopyTemplateError(\n    \"The provided template is not a valid local directory or tarball.\",\n  );\n}\n\nconst pipeline = promisify(stream.pipeline);\n\nasync function extractLocalTarball(\n  tarballPath: string,\n  destPath: string,\n): Promise<void> {\n  try {\n    await pipeline(\n      fs.createReadStream(tarballPath),\n      gunzip(),\n      tar.extract(destPath, { strip: 1 }),\n    );\n  } catch (error: unknown) {\n    throw new CopyTemplateError(","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/remix-run/react-router/blob/1fd704a7dabcbe3ae09d7387b460e6acaba30ec1/packages/create-react-router/copy-template.ts#L121-L157","documentation":"Thrown by copyTemplateFromLocalFilePath() after the path is confirmed to exist but is neither a .tar.gz/.tgz tarball nor a directory. The function first checks the extension for tarball extraction, then fs.statSync().isDirectory() for a direct copy; any other file type (a regular file, a symlink to a file, etc.) reaches the throw. It indicates the local template target is an individual file rather than a project tree.","triggerScenarios":"Passing --template ./README.md, --template ./package.json, or any single regular file that does not end in .tar.gz/.tgz. Also a broken symlink whose target resolves to a file, or a path whose extension was expected to be a tarball but was renamed (e.g. template.zip).","commonSituations":"User selects a file in their editor and pastes its path; template was zipped as .zip instead of .tar.gz; the template directory was replaced by a file of the same name; pointing at a package.json thinking it is the project root.","solutions":["Point --template at the directory containing the project, not a file inside it: --template ./path/to/template-dir.","If the template is a compressed archive, repackage it as .tar.gz or .tgz (tar -czf template.tar.gz -C template-dir .) and pass that path.","If you only have a .zip, extract it first (unzip template.zip) then pass the extracted directory.","Confirm the target with ls -la <path> and ensure it shows 'directory' (d prefix) before rerunning."],"exampleFix":"// before\ncreate-react-router my-app --template ./template.zip\n// after (extract first, then point at the directory)\nunzip template.zip -d template-dir\ncreate-react-router my-app --template ./template-dir","handlingStrategy":"validation","validationCode":"import fs from 'node:fs';\nfunction isLocalDirOrTarball(p: string): boolean {\n  if (!fs.existsSync(p)) return false;\n  if (p.endsWith('.tar.gz') || p.endsWith('.tgz')) return true;\n  try { return fs.statSync(p).isDirectory(); } catch { return false; }\n}\n// if (!isLocalDirOrTarball(template)) error before running the CLI","typeGuard":"function isLocalTemplate(p: string): boolean {\n  return (p.endsWith('.tar.gz') || p.endsWith('.tgz')) || (fs.existsSync(p) && fs.statSync(p).isDirectory());\n}","tryCatchPattern":null,"preventionTips":["Point --template at directories, not individual files.","Keep tarballs in .tar.gz/.tgz format; extract zips before passing them.","Run ls -la on the target to confirm it is a directory (d-prefixed) before scaffolding."],"tags":["cli","template","filesystem","create-react-router"],"backgroundTag":null,"analyzedSha":"1fd704a7dabcbe3ae09d7387b460e6acaba30ec1","analyzedAt":"2026-08-12T13:54:57.804Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}