{"record":{"id":"14af206e4d7c9a96","repo":"vitest-dev/vitest","slug":"the-projects-glob-matched-a-file-relative-paren","errorCode":null,"errorMessage":"The projects glob matched a file \"${relative(parentConfig.root, path)}\", but it should also either start with \"vitest.config\"/\"vite.config\" or match the pattern \"(vitest|vite).*.config.*.\".","messagePattern":"The projects glob matched a file \"(.+?)\", but it should also either start with \"vitest\\.config\"/\"vite\\.config\" or match the pattern \"\\(vitest\\|vite\\)\\.\\*\\.config\\.\\*\\.\"\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/vitest/src/node/projects/resolveProjects.ts","lineNumber":1268,"sourceCode":"\n    const projectsFs = await glob(projectsGlobMatches, globOptions)\n\n    projectsFs.forEach((path) => {\n      // directories are allowed with a glob like `packages/*`\n      // in this case every directory is treated as a project\n      if (path.endsWith('/')) {\n        const configFile = resolveDirectoryConfig(path)\n        if (configFile) {\n          projectsConfigFiles.push(configFile)\n        }\n        else {\n          nonConfigProjectDirectories.push(path)\n        }\n      }\n      else {\n        const name = basename(path)\n        if (!CONFIG_REGEXP.test(name)) {\n          throw new Error(\n            `The projects glob matched a file \"${relative(parentConfig.root, path)}\", `\n            + `but it should also either start with \"vitest.config\"/\"vite.config\" `\n            + `or match the pattern \"(vitest|vite).*.config.*\".`,\n          )\n        }\n        projectsConfigFiles.push(path)\n      }\n    })\n  }\n\n  const projectConfigFiles = Array.from(new Set(projectsConfigFiles))\n\n  return {\n    projectConfigs: projectsOptions,\n    nonConfigDirectories: nonConfigProjectDirectories,\n    configFiles: projectConfigFiles,\n  }\n}","sourceCodeStart":1250,"sourceCodeEnd":1286,"githubUrl":"https://github.com/vitest-dev/vitest/blob/1fa9837ec26533512fdcad8baebf249771bd340a/packages/vitest/src/node/projects/resolveProjects.ts#L1250-L1286","documentation":"Like the direct-file rule (error 285) but applied to glob results: every file a `projects` glob expands to must still satisfy the config naming convention (`CONFIG_REGEXP`). A single non-config match fails the whole glob so unrelated files don't get loaded as projects.","triggerScenarios":"A dynamic glob entry (e.g. `./configs/*`) expands and one of the matched paths' basename fails `CONFIG_REGEXP.test(name)` (non-directory path).","commonSituations":"Overly broad glob catching a README, `.eslintrc`, or other non-config file that happens to live in the same directory.","solutions":["Tighten the glob to match only config files, e.g. `./configs/vitest.config.*` or `./**/vitest.config.{ts,js}`.","Move non-config files out of the globbed directory.","Use the `(vitest|vite).*.config.*` pattern to name environment-specific configs so they pass `CONFIG_REGEXP`."],"exampleFix":"// before: glob matches readme.md\nexport default defineConfig({ test: { projects: ['./configs/*'] } })\n\n// after\nexport default defineConfig({ test: { projects: ['./configs/vitest.*.config.ts'] } })","handlingStrategy":"validation","validationCode":"import { basename } from 'node:path'\nconst CONFIG_REGEXP = /^(vitest|vite)(\\..*)?\\.config\\..+$/\nimport { glob } from 'tinyglobby'\n\nasync function assertGlobMatchesConfigs(root: string, pattern: string) {\n  for (const path of await glob(pattern, { cwd: root, absolute: true })) {\n    const name = basename(path)\n    if (!CONFIG_REGEXP.test(name) && !name.startsWith('vitest.config') && !name.startsWith('vite.config')) {\n      throw new Error(`glob '${pattern}' matched non-config file '${name}'. Tighten the pattern.`)\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Scope project globs to config filenames: `./**/vitest.config.{ts,js}`.","Keep non-config files out of directories you glob over."],"tags":["config","projects","glob"],"backgroundTag":null,"analyzedSha":"1fa9837ec26533512fdcad8baebf249771bd340a","analyzedAt":"2026-08-11T16:11:39.638Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}