vitest-dev/vitest · error · Error

Projects definition references a non-existing file or a…

Error message

Projects definition references a non-existing file or a directory: ${file}

What it means

When `projects` contains a literal string (not a glob), Vitest resolves it relative to the parent config's `root` and verifies the path exists immediately. A missing path is treated as a configuration error rather than silently skipped, so a typo doesn't quietly drop a project.

Solutions

  1. Correct the path so it points to an existing file or directory.
  2. If the path is intentionally optional, use a glob (e.g. `./configs/vitest.*.ts`) which is resolved lazily and tolerates zero matches.
  3. Verify `root` is what you expect (path is resolved against `parentConfig.root`).

Example fix

// before
export default defineConfig({ test: { projects: ['./packages/ap/vitest.conf.ts'] } })

// after (fixed typo)
export default defineConfig({ test: { projects: ['./packages/app/vitest.config.ts'] } })

// after (optional, glob-based)
export default defineConfig({ test: { projects: ['./packages/*/vitest.config.ts'] } })
Defensive patterns

Strategy: validation

Validate before calling

import { existsSync } from 'node:fs'
import { resolve, isAbsolute } from 'node:path'
import pm from 'picomatch'

function assertProjectsPathsExist(root: string, projects: unknown[]) {
  for (const def of projects) {
    if (typeof def !== 'string') continue
    if (pm.isMatch(def, '**') || /[*/?]/.test(def)) continue // glob, resolved lazily
    const file = resolve(root, def.replace('<rootDir>', root))
    if (!existsSync(file)) {
      throw new Error(`projects entry '${def}' does not exist: ${file}`)
    }
  }
}

Prevention

When it happens

Trigger: A `projects` array entry that is a plain string (not a dynamic/glob pattern per `isDynamicPattern`) whose `resolve(root, entry)` fails `existsSync`.

Common situations: Typo in the path; referencing a config file that was renamed or moved; sparse CI checkout missing a sub-project; case-sensitivity mismatch between macOS and Linux.

Related errors


AI-assisted analysis of vitest-dev/vitest@1fa9837ec2 (2026-08-11). Data as JSON: /api/errors/0daa5ae350a435ba. Report an issue: GitHub.

Appendix: source

Thrown at packages/vitest/src/node/projects/resolveProjects.ts:1182

  // custom config files that were specified directly or resolved from a directory
  const projectsConfigFiles: string[] = []

  // custom glob matches that should be resolved as directories or config files
  const projectsGlobMatches: string[] = []

  // directories that don't have a config file inside, but should be treated as projects
  const nonConfigProjectDirectories: string[] = []

  for (const definition of projectsDefinition) {
    if (typeof definition === 'string') {
      const stringOption = definition.replace('<rootDir>', parentConfig.root)
      // if the string doesn't contain a glob, we can resolve it directly
      // ['./vitest.config.js']
      if (!isDynamicPattern(stringOption)) {
        const file = resolve(parentConfig.root, stringOption)

        if (!existsSync(file)) {
          throw new Error(`Projects definition references a non-existing file or a directory: ${file}`)
        }

        const stats = statSync(file)
        // user can specify a config file directly
        if (stats.isFile()) {
          const name = basename(file)
          if (!CONFIG_REGEXP.test(name)) {
            throw new Error(
              `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.`,
            )
          }

          projectsConfigFiles.push(file)
        }
        // user can specify a directory that should be used as a project
        else if (stats.isDirectory()) {
          const configFile = resolveDirectoryConfig(file)

View on GitHub (pinned to 1fa9837ec2)