garrytan/gstack · error · Error
No free test files were discovered.
Error message
No free test files were discovered.
What it means
Thrown by main() in scripts/test-free-shards.ts:292 when collectFreeTestFiles() returns an empty array. collectFreeTestFiles() (lines 156-169) walks the three hardcoded TEST_ROOTS (browse/test, test, make-pdf/test) relative to ROOT (the repo root, resolved at line 31 from import.meta.dir/..), keeps files matching TEST_FILE_REGEX, and excludes any file matching the PAID_EVAL_TESTS regexes (lines 37-44). An empty result means none of those directories existed, none contained a *.test.* file, or every discovered file was classified as a paid-eval test.
Source
Thrown at scripts/test-free-shards.ts:292
function runShard(files: string[], shardNumber: number, totalShards: number): number {
const header = `[test:free] shard ${shardNumber}/${totalShards} (${files.length} files)`;
console.log(header);
const result = spawnSync(process.execPath, buildShardArgs(files), {
cwd: ROOT,
stdio: 'inherit',
env: process.env,
});
if (result.status !== 0) {
console.error(`${header} failed with exit code ${result.status ?? 1}`);
}
return result.status ?? 1;
}
function main(): number {
const options = parseCliOptions(process.argv.slice(2));
const allFiles = collectFreeTestFiles();
if (allFiles.length === 0) {
throw new Error('No free test files were discovered.');
}
let files = allFiles;
let curationReport: CurationResult | null = null;
if (options.windowsOnly) {
curationReport = curateWindowsSafe(allFiles);
files = curationReport.safe;
console.log(`[test:free] curated ${files.length} Windows-safe tests (${curationReport.excluded.length} excluded)`);
if (options.listOnly && curationReport.excluded.length > 0) {
console.log('\nExcluded (POSIX-fragile):');
for (const { file, reason } of curationReport.excluded) {
console.log(` - ${file} [${reason}]`);
}
}
}
if (options.listOnly) {
console.log(`\nDiscovered ${files.length} test files.`);View on GitHub (pinned to 94993f7401)
Solutions
- Confirm cwd is the repo root: the script resolves ROOT from import.meta.dir/.., so run it as `bun run scripts/test-free-shards.ts` from the repo root, or pass rootDir if calling collectFreeTestFiles programmatically.
- Verify the three test roots exist: ls browse/test test make-pdf/test — if any is missing, the checkout is incomplete or the repo layout changed.
- If the layout changed, update the TEST_ROOTS constant at line 32 to the new paths, or pass a custom rootDir to the exported collectFreeTestFiles(rootDir).
- Run with --list after fixing the path; if --list prints files but main() still throws, the issue is that collectFreeTestFiles is being called with a wrong rootDir in your integration.
- If all files are being excluded as paid-eval, inspect PAID_EVAL_TESTS (lines 37-44) — a too-greedy regex may be over-matching your test filenames.
Example fix
// before: run from a subdirectory, ROOT resolves wrong
# (cwd is browse/)
bun run ../scripts/test-free-shards.ts
// after: run from repo root
bun run scripts/test-free-shards.ts --list
// programmatic fix: pass the correct root explicitly
import { collectFreeTestFiles } from './scripts/test-free-shards.ts';
const files = collectFreeTestFiles('/abs/path/to/repo/root'); Defensive patterns
Strategy: validation
Validate before calling
// Verify the three test roots exist and contain at least one *.test.* file before
// invoking main(), so the failure is reported as a path issue, not a surprise throw.
import * as fs from 'fs';
import * as path from 'path';
const ROOTS = ['browse/test', 'test', 'make-pdf/test'];
const missing = ROOTS.filter(r => !fs.existsSync(path.join(process.cwd(), r)));
if (missing.length === ROOTS.length) {
console.error(`No test roots found under cwd ${process.cwd()}. Expected: ${ROOTS.join(', ')}`);
process.exit(2);
}
// Or call the exported function directly and react to empty:
import { collectFreeTestFiles } from './scripts/test-free-shards.ts';
if (collectFreeTestFiles().length === 0) {
console.error('No free test files; check TEST_ROOTS and PAID_EVAL_TESTS in the script.');
process.exit(2);
} Type guard
// Guard the directory-listing boundary the script itself does not guard.
function hasTestFiles(dir: string): boolean {
if (!fs.existsSync(dir)) return false;
// walkTestFiles is private; approximate with a shallow + recursive check via the
// exported collectFreeTestFiles(rootDir) instead for full fidelity.
return collectFreeTestFiles(path.dirname(dir)).length > 0;
} Try / catch
// Treat empty discovery as a configuration error (exit 2), not a test failure.
if (import.meta.main) {
try { process.exitCode = main(); }
catch (err) {
const msg = (err as Error).message;
if (msg.startsWith('No free test files')) {
console.error('[test-free-shards] discovery failure — verify cwd is repo root and test/ trees exist');
process.exitCode = 2;
} else { console.error(msg); process.exitCode = 1; }
}
} Prevention
- Always invoke the script from the repo root; it resolves ROOT from import.meta.dir/.., so a wrong cwd silently zeroes discovery.
- In CI, run a `--list` step before the sharded run — it exits 0 with file output when discovery works and surfaces the throw early.
- When restructuring the repo, update TEST_ROOTS (line 32) in the same commit and add a grep test that asserts at least one file is found per root.
- If you import collectFreeTestFiles programmatically, pass an explicit absolute rootDir rather than relying on the import.meta.dir default.
When it happens
Trigger: Running the script from a directory that is not the repo root, so path.join(rootDir, 'browse/test') etc. point at non-existent paths (fs.existsSync at line 160 silently skips them). Running in a worktree or checkout where the test/ trees were not copied. Running after a monorepo restructure that moved browse/test or make-pdf/test. Running on a fresh export that stripped .test.ts files. Or, less likely, every test file matches one of the PAID_EVAL_TESTS regexes (e.g. a checkout containing only security-review-fullstack.test.ts).
Common situations: CI checked out into a subdirectory and invoked the script with a relative cwd. A contributor runs `bun run scripts/test-free-shards.ts` from inside browse/ rather than the repo root. A release archive excluded test directories. The repo was cloned with --depth or a sparse-checkout that omitted test/. A rename of browse/test → browse/tests (plural) was not reflected in the TEST_ROOTS constant.
Related errors
- Unknown argument: ${arg}
- --shard must be between 1 and ${shards.length}. Received: ${
- Skill "${name}" not found in any tier.
- Skill "${name}" not found.
- Skill "${name}" has no script.test.ts at ${testFile}
AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12).
Data as JSON: /api/errors/fd89246e92afa9ef.
Report an issue: GitHub.