{"record":{"id":"aa874801fc152fe6","repo":"koala73/worldmonitor","slug":"the-selected-shard-contains-no-test-files","errorCode":null,"errorMessage":"The selected shard contains no test files","messagePattern":"The selected shard contains no test files","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/run-data-tests.mjs","lineNumber":63,"sourceCode":"  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({\n      files: selected.map((file) => resolve(file)),\n      concurrency: Number(values.concurrency),\n      timeout: 120000,\n      execArgv: ['--import', 'tsx'],\n      testNamePatterns: values['test-name-pattern'],\n      env,","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/scripts/run-data-tests.mjs#L45-L81","documentation":"After partitioning the resolved files across `total` shards, the runner selects shard `index`. If that particular shard ends up with zero files (possible even when index <= total and total <= files.length, e.g. with skewed timing-based partitioning), it throws 'The selected shard contains no test files' so CI fails loudly instead of passing vacuously.","triggerScenarios":"Running with `--shard N/T` where the timing-based partition assigns no files to shard N — e.g. more shards than meaningfully distributable files, or a timing JSON (`timingPath`) that marks other files much slower so shard N gets nothing.","commonSituations":"Timing data file stale or skewed after test changes, pushing all files into other shards; shard index chosen manually in CI that no longer receives files after the suite changed; rerunning a shard in isolation after the file set shrank.","solutions":["Rerun with `--list` to inspect which files each shard receives, then choose a shard index that has files","Regenerate/refresh the timing data file so partitioning is balanced, or delete stale timing data if regeneration is automatic","Reduce the shard TOTAL or pick a different INDEX (e.g. --shard 1/2 instead of --shard 3/3)"],"exampleFix":"// before\nnode scripts/run-data-tests.mjs --shard 4/4 'tests/data/**/*.test.mjs'\n// after (verify with --list first)\nnode scripts/run-data-tests.mjs --list --shard 1/4 'tests/data/**/*.test.mjs'\nnode scripts/run-data-tests.mjs --shard 1/4 'tests/data/**/*.test.mjs'","handlingStrategy":"validation","validationCode":"const selected = partitionTests(files, durations, total)[index - 1];\nif (!selected.length && total > 1) {\n  // fall back to a shard known to have files, or fail with diagnostics\n  console.error(JSON.stringify(partitionTests(files, durations, total).map(p => p.length)));\n  throw new Error(`Shard ${index}/${total} empty; sizes above`);\n}","typeGuard":"const shardHasFiles = (files, durations, index, total) => partitionTests(files, durations, total)[index - 1].length > 0;","tryCatchPattern":"try {\n  await run();\n} catch (err) {\n  if (err.message === 'The selected shard contains no test files') {\n    console.error('Rebalance shards: refresh timing data or lower shard TOTAL; inspect with --list.');\n    process.exit(2);\n  }\n  throw err;\n}","preventionTips":["Run with --list after changing the test file set to confirm every shard is non-empty","Regenerate the timing JSON whenever tests are added/removed/renamed so partitioning stays balanced","Keep shard TOTAL modest relative to file count; prefer fewer, guaranteed non-empty shards","Avoid hardcoding shard INDEX in CI when the partition can shift; iterate over indices with files"],"tags":["cli","sharding","empty-result","test-runner"],"backgroundTag":"empty-result-set","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"}