koala73/worldmonitor · error · Error
Shard count exceeds the test file count
Error message
Shard count exceeds the test file count
What it means
When --shard N/T is given, the runner partitions the resolved test files into t shards. If the requested total shard count t exceeds the number of resolved test files, some shards would be empty by construction, so it throws 'Shard count exceeds the test file count' to abort the run.
Solutions
- Lower the TOTAL in --shard to at most the number of matched files, e.g. --shard 1/5 for 5 files
- Add more files/globs so the count meets or exceeds the shard total
- Check which files the current globs resolve to and reconcile with the CI matrix shard count
Example fix
// before (3 files resolved) node scripts/run-data-tests.mjs --shard 2/8 'tests/data/**/*.test.mjs' // after node scripts/run-data-tests.mjs --shard 2/3 'tests/data/**/*.test.mjs'
Defensive patterns
Strategy: validation
Validate before calling
import { globSync } from 'node:fs';
const fileCount = new Set(globs.flatMap(g => globSync(g))).size;
if (shardTotal > fileCount) {
throw new Error(`--shard TOTAL (${shardTotal}) exceeds ${fileCount} resolved test files`);
} Type guard
const shardCountIsValid = (total, files) => Number.isSafeInteger(total) && total >= 1 && total <= files.length;
Try / catch
try {
await run();
} catch (err) {
if (err.message === 'Shard count exceeds the test file count') {
console.error(`Reduce --shard TOTAL to <= number of matched files (matched: ${files.length}).`);
process.exit(2);
}
throw err;
} Prevention
- Derive the CI matrix shard count from the same glob the runner uses, or cap TOTAL dynamically
- When removing test files, audit shard TOTAL values in CI matrix configs
- Keep one source of truth for the file globs shared between the matrix definition and the runner invocation
When it happens
Trigger: Running `node scripts/run-data-tests.mjs --shard 4/8 <globs>` where the globs resolve to fewer than 8 files, e.g. passing 5 files with `--shard 1/8`.
Common situations: Bumping CI matrix parallelism without shrinking the file list; narrowing the test globs (fewer files) while the CI matrix still requests more shards; a --shard TOTAL left at an old larger value after test files were removed.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- The selected shard contains no test files
- Supply test files or globs
- No test files match ${pattern}
- global fetch is unavailable — Node 18+ is required
- Redis not configured
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15).
Data as JSON: /api/errors/f08cd8585b671ea7.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/run-data-tests.mjs:61
let index = 1;
let total = 1;
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'],View on GitHub (pinned to 7d06c8633d)