{"id":"8650131dd6757c04","repo":"vitest-dev/vitest","slug":"vitest-files-not-found","errorCode":"VITEST_FILES_NOT_FOUND","errorMessage":"No test files found","messagePattern":"No test files found","errorType":"exception","errorClass":"FilesNotFoundError","httpStatus":null,"severity":"error","filePath":"packages/vitest/src/node/core.ts","lineNumber":868,"sourceCode":"        specifications = specifications.filter(({ testModule }) => {\n          return !testModule || testModule.task.mode !== 'skip'\n        })\n      }\n\n      // if run with --changed, don't exit if no tests are found\n      if (!specifications.length) {\n        await this._traces.$('vitest.test_run', async () => {\n          await this._testRun.start([])\n          await this.coverageProvider?.onTestRunStart?.()\n          const coverage = await this.coverageProvider?.generateCoverage?.({ allTestsRun: true })\n\n          await this._testRun.end([], [], coverage)\n          // Report coverage for uncovered files\n          await this.reportCoverage(coverage, true)\n        })\n\n        if (!this.config.watch || !(this.config.changed || this.config.related?.length)) {\n          throw new FilesNotFoundError()\n        }\n      }\n\n      let testModules: TestRunResult = {\n        testModules: [],\n        unhandledErrors: [],\n      }\n\n      if (specifications.length) {\n        // populate once, update cache on watch\n        await this.cache.stats.populateStats(this.config.root, specifications)\n\n        testModules = await this.runFiles(specifications, true)\n      }\n\n      if (this.config.watch) {\n        await this.report('onWatcherStart')\n      }","sourceCodeStart":850,"sourceCodeEnd":886,"githubUrl":"https://github.com/vitest-dev/vitest/blob/d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09/packages/vitest/src/node/core.ts#L850-L886","documentation":"FilesNotFoundError (errors.ts, code VITEST_FILES_NOT_FOUND) is thrown at core.ts:868 when specifications.length === 0 after include/exclude glob resolution. It is suppressed in watch mode combined with --changed/--related so a transient empty state doesn't kill the watcher. Any other time, vitest treats zero matched test files as a hard failure so misconfigurations aren't silently green.","triggerScenarios":"include/exclude globs match zero files; CLI --include/--exclude override strips everything; running from a directory with no matching test files; project test.include empty or wrong root.","commonSituations":"Wrong cwd; misconfigured include glob; test files renamed/removed; monorepo filter that excludes all packages.","solutions":["Check test.include / test.exclude glob patterns and the --dir / cwd you run from.","Verify matching test files exist with the expected extension (.test.ts, .spec.js, etc.).","Pass --passWithNoTests (or set passWithNoTests: true) if an empty run is intentional.","In watch mode, use --changed/--related to allow empty runs without error."],"exampleFix":"// before\ntest: { include: ['src/**/*.test.ts'] } // tests live in tests/\n\n// after\ntest: { include: ['{src,tests}/**/*.test.ts'] }","handlingStrategy":"validation","validationCode":"const { glob } = await import('tinyglobby')\nconst matches = await glob(testInclude, { cwd: root, absolute: false })\nif (matches.length === 0 && !passWithNoTests) {\n  throw new Error('Globs match no files; check test.include')\n}","typeGuard":"function hasTestFiles(files: string[]): boolean {\n  return files.length > 0\n}","tryCatchPattern":"try { await vitest.runFiles(specs) }\ncatch (e) {\n  if (e?.code === 'VITEST_FILES_NOT_FOUND' && options.allowEmpty) return { empty: true }\n  throw e\n}","preventionTips":["Sanity-check that include globs match files before running in CI.","Set passWithNoTests: true when empty runs are acceptable.","Run vitest from the repo root or pass --dir explicitly."],"tags":["glob","discovery","config","files"],"analyzedSha":"d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09","analyzedAt":"2026-08-03T20:23:56.861Z","schemaVersion":2}