{"record":{"id":"fd89246e92afa9ef","repo":"garrytan/gstack","slug":"no-free-test-files-were-discovered","errorCode":null,"errorMessage":"No free test files were discovered.","messagePattern":"No free test files were discovered\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/test-free-shards.ts","lineNumber":292,"sourceCode":"function runShard(files: string[], shardNumber: number, totalShards: number): number {\n  const header = `[test:free] shard ${shardNumber}/${totalShards} (${files.length} files)`;\n  console.log(header);\n  const result = spawnSync(process.execPath, buildShardArgs(files), {\n    cwd: ROOT,\n    stdio: 'inherit',\n    env: process.env,\n  });\n  if (result.status !== 0) {\n    console.error(`${header} failed with exit code ${result.status ?? 1}`);\n  }\n  return result.status ?? 1;\n}\n\nfunction main(): number {\n  const options = parseCliOptions(process.argv.slice(2));\n  const allFiles = collectFreeTestFiles();\n  if (allFiles.length === 0) {\n    throw new Error('No free test files were discovered.');\n  }\n\n  let files = allFiles;\n  let curationReport: CurationResult | null = null;\n  if (options.windowsOnly) {\n    curationReport = curateWindowsSafe(allFiles);\n    files = curationReport.safe;\n    console.log(`[test:free] curated ${files.length} Windows-safe tests (${curationReport.excluded.length} excluded)`);\n    if (options.listOnly && curationReport.excluded.length > 0) {\n      console.log('\\nExcluded (POSIX-fragile):');\n      for (const { file, reason } of curationReport.excluded) {\n        console.log(`  - ${file}  [${reason}]`);\n      }\n    }\n  }\n\n  if (options.listOnly) {\n    console.log(`\\nDiscovered ${files.length} test files.`);","sourceCodeStart":274,"sourceCodeEnd":310,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/scripts/test-free-shards.ts#L274-L310","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before: run from a subdirectory, ROOT resolves wrong\n# (cwd is browse/)\nbun run ../scripts/test-free-shards.ts\n// after: run from repo root\nbun run scripts/test-free-shards.ts --list\n\n// programmatic fix: pass the correct root explicitly\nimport { collectFreeTestFiles } from './scripts/test-free-shards.ts';\nconst files = collectFreeTestFiles('/abs/path/to/repo/root');","handlingStrategy":"validation","validationCode":"// Verify the three test roots exist and contain at least one *.test.* file before\n// invoking main(), so the failure is reported as a path issue, not a surprise throw.\nimport * as fs from 'fs';\nimport * as path from 'path';\nconst ROOTS = ['browse/test', 'test', 'make-pdf/test'];\nconst missing = ROOTS.filter(r => !fs.existsSync(path.join(process.cwd(), r)));\nif (missing.length === ROOTS.length) {\n  console.error(`No test roots found under cwd ${process.cwd()}. Expected: ${ROOTS.join(', ')}`);\n  process.exit(2);\n}\n// Or call the exported function directly and react to empty:\nimport { collectFreeTestFiles } from './scripts/test-free-shards.ts';\nif (collectFreeTestFiles().length === 0) {\n  console.error('No free test files; check TEST_ROOTS and PAID_EVAL_TESTS in the script.');\n  process.exit(2);\n}","typeGuard":"// Guard the directory-listing boundary the script itself does not guard.\nfunction hasTestFiles(dir: string): boolean {\n  if (!fs.existsSync(dir)) return false;\n  // walkTestFiles is private; approximate with a shallow + recursive check via the\n  // exported collectFreeTestFiles(rootDir) instead for full fidelity.\n  return collectFreeTestFiles(path.dirname(dir)).length > 0;\n}","tryCatchPattern":"// Treat empty discovery as a configuration error (exit 2), not a test failure.\nif (import.meta.main) {\n  try { process.exitCode = main(); }\n  catch (err) {\n    const msg = (err as Error).message;\n    if (msg.startsWith('No free test files')) {\n      console.error('[test-free-shards] discovery failure — verify cwd is repo root and test/ trees exist');\n      process.exitCode = 2;\n    } else { console.error(msg); process.exitCode = 1; }\n  }\n}","preventionTips":["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."],"tags":["test-discovery","filesystem","cwd","ci","sharding"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}