{"id":"0f5640a129e2471d","repo":"vitest-dev/vitest","slug":"the-file-relative-parentconfig-root-file-mu","errorCode":null,"errorMessage":"The file \"${relative(parentConfig.root, file)}\" must start with \"vitest.config\"/\"vite.config\" or match the pattern \"(vitest|vite).*.config.*\" to be a valid project config.","messagePattern":"The file \"(.+?)\" must start with \"vitest\\.config\"/\"vite\\.config\" or match the pattern \"\\(vitest\\|vite\\)\\.\\*\\.config\\.\\*\" to be a valid project config\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/vitest/src/node/projects/resolveProjects.ts","lineNumber":993,"sourceCode":"\n  for (const definition of projectsDefinition) {\n    if (typeof definition === 'string') {\n      const stringOption = definition.replace('<rootDir>', parentConfig.root)\n      // if the string doesn't contain a glob, we can resolve it directly\n      // ['./vitest.config.js']\n      if (!isDynamicPattern(stringOption)) {\n        const file = resolve(parentConfig.root, stringOption)\n\n        if (!existsSync(file)) {\n          throw new Error(`Projects definition references a non-existing file or a directory: ${file}`)\n        }\n\n        const stats = statSync(file)\n        // user can specify a config file directly\n        if (stats.isFile()) {\n          const name = basename(file)\n          if (!CONFIG_REGEXP.test(name)) {\n            throw new Error(\n              `The file \"${relative(parentConfig.root, file)}\" must start with \"vitest.config\"/\"vite.config\" `\n              + `or match the pattern \"(vitest|vite).*.config.*\" to be a valid project config.`,\n            )\n          }\n\n          projectsConfigFiles.push(file)\n        }\n        // user can specify a directory that should be used as a project\n        else if (stats.isDirectory()) {\n          const configFile = resolveDirectoryConfig(file)\n          if (configFile) {\n            projectsConfigFiles.push(configFile)\n          }\n          else {\n            const directory = file.at(-1) === '/' ? file : `${file}/`\n            nonConfigProjectDirectories.push(directory)\n          }\n        }","sourceCodeStart":975,"sourceCodeEnd":1011,"githubUrl":"https://github.com/vitest-dev/vitest/blob/d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09/packages/vitest/src/node/projects/resolveProjects.ts#L975-L1011","documentation":"When a string entry points to an existing file (resolveProjects.ts:991-997), Vitest checks the basename against CONFIG_REGEXP = /^vite(?:st)?(?:\\.[\\w-]+)?\\.config\\./, which accepts vitest.config.*, vite.config.*, vitest.<scope>.config.*, vite.<scope>.config.*. Files that don't match (e.g. random.ts, tsconfig.json, package.json) are rejected so config loader doesn't choke on them.","triggerScenarios":"Listing a non-config file in projects: ['package.json'], ['src/index.ts'], ['random.config.js'] that doesn't start with vite/vitest.config.; pointing at a vite.config file with an unexpected prefix.","commonSituations":"Confusing a project source file with a config file; pointing projects at a filename that almost matches (e.g. 'viteconfig.ts' missing the dot); a glob that accidentally matches a non-config file.","solutions":["Rename or point to a file matching vitest.config.* / vite.config.* / vitest.<scope>.config.* / vite.<scope>.config.*.","If you meant a directory, add a trailing slash so it's resolved as a directory project.","For globs that over-match, constrain the pattern (e.g. 'packages/*/vitest.config.ts')."],"exampleFix":"// before\nexport default defineConfig({ test: { projects: ['./package.json'] } })\n\n// after\nexport default defineConfig({ test: { projects: ['./vitest.config.ts'] } })","handlingStrategy":"validation","validationCode":"const CONFIG_REGEXP = /^vite(?:st)?(?:\\.[\\w-]+)?\\.config\\./\nfor (const def of definitions) {\n  if (typeof def === 'string' && !def.endsWith('/') && existsSync(resolve(root, def))) {\n    if (!CONFIG_REGEXP.test(basename(def))) throw new Error(`'${def}' is not a valid config file name`)\n  }\n}","typeGuard":"function isValidConfigName(name: string): boolean {\n  return /^vite(?:st)?(?:\\.[\\w-]+)?\\.config\\./.test(name)\n}","tryCatchPattern":null,"preventionTips":["Follow the vite.config.* / vitest.config.* naming convention.","Use a trailing slash to reference a directory project.","Tighten project globs so they only match real config files."],"tags":["projects","config-file","naming","configuration","validation"],"analyzedSha":"d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09","analyzedAt":"2026-08-03T20:23:56.861Z","schemaVersion":2}