{"record":{"id":"5af73e866b21bb70","repo":"koala73/worldmonitor","slug":"no-test-files-match-pattern","errorCode":null,"errorMessage":"No test files match ${pattern}","messagePattern":"No test files match (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/run-data-tests.mjs","lineNumber":57,"sourceCode":"  });\n  if (!/^[1-9]\\d*$/.test(values.concurrency) || !Number.isSafeInteger(Number(values.concurrency))) {\n    throw new Error('Concurrency must be a positive integer');\n  }\n  let index = 1;\n  let total = 1;\n  if (values.shard) {\n    const match = /^([1-9]\\d*)\\/([1-9]\\d*)$/.exec(values.shard);\n    if (!match) throw new Error('Shard must be INDEX/TOTAL (for example 1/2)');\n    [index, total] = match.slice(1).map(Number);\n    if (!Number.isSafeInteger(index) || !Number.isSafeInteger(total) || index > total) {\n      throw new Error('Shard index must be between 1 and TOTAL');\n    }\n  }\n  if (!positionals.length) throw new Error('Supply test files or globs');\n  const files = [...new Set(positionals.flatMap((pattern) => {\n    const matches = (existsSync(pattern) && statSync(pattern).isFile() ? [pattern] : globSync(pattern))\n      .map((file) => file.split(sep).join('/'));\n    if (!matches.length) throw new Error(`No test files match ${pattern}`);\n    return matches;\n  }))];\n  const durations = JSON.parse(readFileSync(timingPath, 'utf8'));\n  if (total > files.length) throw new Error('Shard count exceeds the test file count');\n  const selected = partitionTests(files, durations, total)[index - 1];\n  if (!selected.length) throw new Error('The selected shard contains no test files');\n  if (values.list) {\n    console.log(JSON.stringify(selected));\n    return 0;\n  }\n  console.log(`Data tests: ${selected.length}/${files.length} files, shard ${index}/${total}, concurrency ${values.concurrency}`);\n  const env = { ...process.env };\n  // This is a new test run, even when a contract test invokes the CLI.\n  delete env.NODE_TEST_CONTEXT;\n  const timingFd = values.timings ? openSync(values.timings, 'w') : undefined;\n  let success = false;\n  try {\n    const events = run({","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/scripts/run-data-tests.mjs#L39-L75","documentation":"For each positional argument, run-data-tests.mjs first checks if the pattern is an existing file, otherwise expands it with globSync. If a pattern expands to zero matches it throws `No test files match ${pattern}` so typos or wrong paths fail fast instead of silently running zero tests.","triggerScenarios":"Calling the runner with a positional that is neither an existing file nor a matching glob, e.g. `node scripts/run-data-tests.mjs 'tests/data/*.spec.mjs'` when no .spec.mjs files exist, or a typo like 'tests/data/**/*test.mjs'.","commonSituations":"Working from the wrong working directory so relative globs match nothing; renaming/moving test directories so stale globs in CI configs break; quoting issues where the shell already expanded (or failed to expand) the glob; case-sensitive filesystem mismatches.","solutions":["Verify the glob from the same working directory: `ls tests/data/*.test.mjs` — fix typos or path prefixes","Check that the command runs from the repository root (globs are relative to cwd)","If a file was intentionally passed, confirm it exists (`test -f <path>`); if a glob is correct, confirm the test files actually exist in the checkout"],"exampleFix":"// before\nnode scripts/run-data-tests.mjs 'tests/unit/**/*.test.js'\n// after (match the actual layout)\nnode scripts/run-data-tests.mjs 'tests/unit/**/*.test.mjs'","handlingStrategy":"validation","validationCode":"import { existsSync, statSync, globSync } from 'node:fs';\nfor (const pattern of patterns) {\n  const ok = (existsSync(pattern) && statSync(pattern).isFile()) || globSync(pattern).length > 0;\n  if (!ok) throw new Error(`No test files match ${pattern} (cwd=${process.cwd()})`);\n}","typeGuard":"const patternMatchesFiles = (pattern) =>\n  (existsSync(pattern) && statSync(pattern).isFile()) || globSync(pattern).length > 0;","tryCatchPattern":"try {\n  await run();\n} catch (err) {\n  const m = err.message.match(/^No test files match (.+)$/);\n  if (m) {\n    console.error(`Glob ${m[1]} matched nothing. cwd=${process.cwd()}. Check the path and that test files exist.`);\n    process.exit(2);\n  }\n  throw err;\n}","preventionTips":["Run globs from the repository root; print cwd when debugging","Keep glob patterns in a single config/variable and verify them with `ls` or globSync in a dry run","When renaming/moving test files, grep CI configs and scripts for the old paths","Quote globs on the command line so the shell does not pre-expand them inconsistently"],"tags":["cli","glob","file-not-found","test-runner"],"backgroundTag":"file-not-found","analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-09-15T16:44:39.439Z","contentChangedAt":"2026-09-15T16:44:39.439Z","schemaVersion":2},"datasetVersion":"2026-09-15T18:17:12.389Z"}