koala73/worldmonitor · error · Error
The selected shard contains no test files
Error message
The selected shard contains no test files
What it means
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.
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)
Example fix
// before node scripts/run-data-tests.mjs --shard 4/4 'tests/data/**/*.test.mjs' // after (verify with --list first) node scripts/run-data-tests.mjs --list --shard 1/4 'tests/data/**/*.test.mjs' node scripts/run-data-tests.mjs --shard 1/4 'tests/data/**/*.test.mjs'
Defensive patterns
Strategy: validation
Validate before calling
const selected = partitionTests(files, durations, total)[index - 1];
if (!selected.length && total > 1) {
// fall back to a shard known to have files, or fail with diagnostics
console.error(JSON.stringify(partitionTests(files, durations, total).map(p => p.length)));
throw new Error(`Shard ${index}/${total} empty; sizes above`);
} Type guard
const shardHasFiles = (files, durations, index, total) => partitionTests(files, durations, total)[index - 1].length > 0;
Try / catch
try {
await run();
} catch (err) {
if (err.message === 'The selected shard contains no test files') {
console.error('Rebalance shards: refresh timing data or lower shard TOTAL; inspect with --list.');
process.exit(2);
}
throw err;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.
Related errors
- Shard count exceeds the test file count
- Supply test files or globs
- No test files match ${pattern}
- global fetch is unavailable — Node 18+ is required
- --args must be valid JSON: ${err.message}
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15).
Data as JSON: /api/errors/aa874801fc152fe6.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/run-data-tests.mjs:63
if (values.shard) {
const match = /^([1-9]\d*)\/([1-9]\d*)$/.exec(values.shard);
if (!match) throw new Error('Shard must be INDEX/TOTAL (for example 1/2)');
[index, total] = match.slice(1).map(Number);
if (!Number.isSafeInteger(index) || !Number.isSafeInteger(total) || index > total) {
throw new Error('Shard index must be between 1 and TOTAL');
}
}
if (!positionals.length) throw new Error('Supply test files or globs');
const files = [...new Set(positionals.flatMap((pattern) => {
const matches = (existsSync(pattern) && statSync(pattern).isFile() ? [pattern] : globSync(pattern))
.map((file) => file.split(sep).join('/'));
if (!matches.length) throw new Error(`No test files match ${pattern}`);
return matches;
}))];
const durations = JSON.parse(readFileSync(timingPath, 'utf8'));
if (total > files.length) throw new Error('Shard count exceeds the test file count');
const selected = partitionTests(files, durations, total)[index - 1];
if (!selected.length) throw new Error('The selected shard contains no test files');
if (values.list) {
console.log(JSON.stringify(selected));
return 0;
}
console.log(`Data tests: ${selected.length}/${files.length} files, shard ${index}/${total}, concurrency ${values.concurrency}`);
const env = { ...process.env };
// This is a new test run, even when a contract test invokes the CLI.
delete env.NODE_TEST_CONTEXT;
const timingFd = values.timings ? openSync(values.timings, 'w') : undefined;
let success = false;
try {
const events = run({
files: selected.map((file) => resolve(file)),
concurrency: Number(values.concurrency),
timeout: 120000,
execArgv: ['--import', 'tsx'],
testNamePatterns: values['test-name-pattern'],
env,View on GitHub (pinned to 7d06c8633d)