fix(workers): use require.main === module guard so require() doesn't start workers#239
Merged
therealbrad merged 1 commit intomainfrom Apr 23, 2026
Merged
fix(workers): use require.main === module guard so require() doesn't start workers#239therealbrad merged 1 commit intomainfrom
therealbrad merged 1 commit intomainfrom
Conversation
…start workers
The previous guard combined an ESM-style import.meta check with a CJS
fallback:
if (
(typeof import.meta !== "undefined" &&
import.meta.url === pathToFileURL(process.argv[1]).href) ||
typeof import.meta === "undefined" ||
(import.meta as any).url === undefined
) { startWorker()... }
esbuild compiles each worker to CommonJS (platform: node, format: cjs)
and polyfills `import.meta` as a plain object whose `.url` is
`undefined`. At runtime that makes `import.meta.url === void 0` always
true, so the guard always fires — meaning `require("./forecastWorker")`
unintentionally invokes `startWorker()` and all its connection logic.
Three workers (forecastWorker, repoCacheWorker, testmoImportWorker)
call `process.exit(1)` synchronously when Valkey is unreachable, so the
CI smoke test added in #237 died mid-loop and releases blocked on the
smoke-test step.
Fix: replace with the canonical CJS pattern `require.main === module`.
esbuild preserves `require`/`module` in CJS output, and the smoke test
can now `require()` each worker without triggering startup side effects
— matching the intent stated in the original comment.
Drops the `pathToFileURL` import from each worker (was only used by
the old guard).
Contributor
Author
|
🎉 This PR is included in version 0.22.9 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
This was referenced Apr 23, 2026
therealbrad
added a commit
that referenced
this pull request
Apr 24, 2026
…t run scheduling (#241) * fix(workers): use require.main === module guard so require() doesn't start workers The previous guard combined an ESM-style import.meta check with a CJS fallback: if ( (typeof import.meta !== "undefined" && import.meta.url === pathToFileURL(process.argv[1]).href) || typeof import.meta === "undefined" || (import.meta as any).url === undefined ) { startWorker()... } esbuild compiles each worker to CommonJS (platform: node, format: cjs) and polyfills `import.meta` as a plain object whose `.url` is `undefined`. At runtime that makes `import.meta.url === void 0` always true, so the guard always fires — meaning `require("./forecastWorker")` unintentionally invokes `startWorker()` and all its connection logic. Three workers (forecastWorker, repoCacheWorker, testmoImportWorker) call `process.exit(1)` synchronously when Valkey is unreachable, so the CI smoke test added in #237 died mid-loop and releases blocked on the smoke-test step. Fix: replace with the canonical CJS pattern `require.main === module`. esbuild preserves `require`/`module` in CJS output, and the smoke test can now `require()` each worker without triggering startup side effects — matching the intent stated in the original comment. Drops the `pathToFileURL` import from each worker (was only used by the old guard). * fix(workers): guard generateFromUrlWorker + stub env in smoke test v0.22.9 smoke test surfaced two issues hidden behind the main-guard bug fixed in the previous commit: 1. generateFromUrlWorker.ts had no main guard at all — it called startGenerateFromUrlWorker() unconditionally at module scope, so require()'ing it in the smoke test attempted to construct a BullMQ Worker with no Valkey and crashed. Wrapped in `if (require.main === module)` to match the other 14 workers. 2. env.js validates DATABASE_URL / NEXTAUTH_SECRET / NEXTAUTH_URL at module-load time via @t3-oss/env-nextjs, so any worker whose transitive imports reach env.js (syncWorker, elasticsearchReindexWorker, copyMoveWorker, duplicateScanWorker, magicSelectWorker) threw during require with zod 'invalid_type' errors. Added dummy env shims at the top of smoke-test-workers.js using `||=` so real CI-provided values still win. The smoke test is verifying module-graph integrity, not runtime config correctness. Together with the main-guard fix, the smoke test should now complete cleanly for every worker. * chore(workers): shorten NEXTAUTH_SECRET stub to fit prettier printWidth * fix(scheduler): add require.main guard so smoke-test require() doesn't run scheduling scheduler.ts called scheduleJobs() at module top-level, so the v0.22.10 smoke test's require() of dist/scheduler.js tried to connect to Valkey and exited 1 ('Required queues are not initialized. Cannot schedule jobs.') even with SKIP_VALKEY_CONNECTION=true (which makes queues no-op but still marks them unavailable). Same pattern as the workers fix in #239/#240: gate the runtime code with `if (require.main === module)`. Production start-workers.sh invokes scheduler directly via `tsx scheduler.ts`, so require.main IS module at runtime and scheduleJobs() still runs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
import.meta.urlmain guard in all 14 workers with the canonical CJS patternrequire.main === module.pathToFileURLimport from each worker.Why it matters — unblocks releases
PR #237's worker smoke test has been failing on every release since it merged. Root cause: esbuild compiles workers to CommonJS and polyfills
import.metaas a plain object whose.urlisundefined. The old guard was:At runtime
import.meta.url === void 0is always true in the CJS output, so the guard always fires — meaningrequire("./forecastWorker")actually runsstartWorker(), opens Valkey connections, etc. Three workers (forecastWorker,repoCacheWorker,testmoImportWorker) thenprocess.exit(1)synchronously when Valkey isn't reachable, killing the CI smoke test mid-loop.With this change,
require()in the smoke test only loads the module graph — exactly the guarantee #237 was supposed to provide. PM2 still starts each worker normally because it invokes them directly asnode dist/workers/<name>.js(sorequire.main === module).Scope
All 14 worker files in
testplanit/workers/exceptgenerateFromUrlWorker.ts(which didn't have the guard):Net: +26 / -112 lines.
Test plan
node scripts/smoke-test-workers.jspasses against a freshly builtdist/:latest-workersimage (bbva / production pods should come up healthy on next rollout)🤖 Generated with Claude Code