{"record":{"id":"c13e1e0331b730c7","repo":"withastro/astro","slug":"no-files-found-to-copy","errorCode":null,"errorMessage":"No files found to copy","messagePattern":"No files found to copy","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/internal-helpers/src/fs.ts","lineNumber":51,"sourceCode":"\n/**\n * Copies files into a folder keeping the folder structure intact.\n * The resulting file tree will start at the common ancestor.\n *\n * @param {URL[]} files A list of files to copy (absolute path).\n * @param {URL} outDir Destination folder where to copy the files to (absolute path).\n * @param {URL[]} [exclude] A list of files to exclude (absolute path).\n * @returns {Promise<string>} The common ancestor of the copied files.\n */\nexport async function copyFilesToFolder(\n\tfiles: URL[],\n\toutDir: URL,\n\texclude: URL[] = [],\n): Promise<string> {\n\tconst excludeList = exclude.map((url) => fileURLToPath(url));\n\tconst fileList = files.map((url) => fileURLToPath(url)).filter((f) => !excludeList.includes(f));\n\n\tif (files.length === 0) throw new Error('No files found to copy');\n\n\tlet commonAncestor = nodePath.dirname(fileList[0]);\n\tfor (const file of fileList.slice(1)) {\n\t\twhile (!file.startsWith(commonAncestor)) {\n\t\t\tcommonAncestor = nodePath.dirname(commonAncestor);\n\t\t}\n\t}\n\n\tfor (const origin of fileList) {\n\t\tconst dest = new URL(nodePath.relative(commonAncestor, origin), outDir);\n\n\t\tconst realpath = await fs.realpath(origin);\n\t\tconst isSymlink = realpath !== origin;\n\t\tconst isDir = (await fs.stat(origin)).isDirectory();\n\n\t\t// Create directories recursively\n\t\tif (isDir && !isSymlink) {\n\t\t\tawait fs.mkdir(new URL('..', dest), { recursive: true });","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/withastro/astro/blob/d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca/packages/internal-helpers/src/fs.ts#L33-L69","documentation":"Thrown by `copyFilesToDummy`/`copyFilesToFolder` in @astrojs/internal-helpers when the `files` array passed in is empty. The function computes a common ancestor and copies each file; with zero files it cannot proceed (and `fileList[0]` would be undefined).","triggerScenarios":"Passing an empty array, or an array where every entry was filtered out by the `exclude` list. A glob/build step that produced no matching files for a stage that copies assets.","commonSituations":"A `public/` or asset directory that is empty in the current build. An integration's file-collection hook returning `[]`. Overly broad `exclude` removing all candidates.","solutions":["Check `files.length > 0` before calling `copyFilesToFolder` if an empty set is legitimately possible.","Verify the glob/source feeding `files` actually matches entries (log the array).","Loosen the `exclude` list if it is filtering out everything."],"exampleFix":"// before\ncopyFilesToFolder(matchedFiles, outDir, exclude);\n// after\nif (matchedFiles.length > 0) {\n  await copyFilesToFolder(matchedFiles, outDir, exclude);\n} else {\n  logger.warn('No files to copy; skipping.');\n}","handlingStrategy":"validation","validationCode":"if (!Array.isArray(files) || files.length === 0) {\n  throw new Error('No files to copy; check the source glob.');\n}","typeGuard":"function isNonEmptyFileList(files: unknown): files is URL[] {\n  return Array.isArray(files) && files.length > 0;\n}","tryCatchPattern":null,"preventionTips":["Guard callers with a length check when an empty set is valid.","Log the resolved file list before copying for debuggability.","Verify exclude patterns don't over-filter."],"tags":["internal-helpers","filesystem","assets","validation"],"backgroundTag":null,"analyzedSha":"d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca","analyzedAt":"2026-08-12T13:37:29.035Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}