Stale hardcoded `.swc` loader cache silently ships new `"use step"` functions as inline code → zero journaling + CORRUPTED_EVENT_LOG on resume
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 2.4k
- Forks
- 365
- Avg merge
- 2d 11h
- Merged PRs (30d)
- 169
Description
Summary
@workflow/next hardcodes the SWC plugin/loader cache to <projectRoot>/.swc with no invalidation key and no opt-out:
@workflow/next/dist/swc-plugin-cache.js:17—cacheRoot: join(projectRoot, '.swc')@workflow/next/dist/loader.js:187— same path in the loader
In CI/Docker environments where .swc persists between builds (cached layers, persistent build contexts), a newly added "use step" function can compile inline (untransformed) into the workflow bundle while the manifest still discovers and counts it. Nothing fails at build time. At runtime the function executes inline in the workflow body — producing zero workflow_steps journal rows — and every subsequent replay/resume of an affected run dies with CORRUPTED_EVENT_LOG (the inline await diverges the event log).
Verified on workflow@5.0.0-beta.25; the cache code is unchanged in 5.0.0-beta.27.
What makes this nasty
- Build succeeds; manifest counts the new step (
workflows build complete (N steps…)includes it). - The turn that runs the inline function works live — the failure surfaces only later, on wake/resume of parked runs, as
CORRUPTED_EVENT_LOG. - Discovery is not the problem (we confirmed
fastDiscoverEntriesreads fresh; the step count incremented correctly). The stale layer is specifically the SWC loader cache on the compile path that turns"use step"source into registered/proxied steps.
Repro shape (from our production incident, Next.js 16 + Turbopack, Docker on Railway)
- Deploy an app with an existing workflow (steps journal normally).
- Add a new
"use step"function (new file OR inside the existing"use workflow"entry — both reproduce) that the workflow body awaits. - Build in an environment where
.swcsurvives from a previous build. - Observe: manifest counts the new step; at runtime the function executes but journals nothing; wake/resume of a parked run fails with
CORRUPTED_EVENT_LOG. - Add
rm -rf .swcbeforenext build→ same code journals correctly and resumes work (this was our fix, verified by journal assertion + resume test).
We reproduced the failure twice (two independent deploys, once with the step in a new module and once defined inside the workflow entry file) and confirmed the rm -rf .swc fix on the third deploy with the identical source.
Ask
Either (a) include a build-id / source-hash / plugin-version invalidation key in the loader cache, or (b) expose a cacheRoot / no-cache option on withWorkflow() so CI builds can opt out. Today the only remedy is deleting .swc out-of-band, and the failure mode when you don't is silent data-plane corruption on resume.
Happy to provide build logs (step counts, timings) and the journal evidence from our incident if useful.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Inspect @workflow/next/dist/swc-plugin-cache.js:17 and loader.js:187, then reproduce the issue with a persistent .swc directory and a newly added "use step" function. Compare the behavior with the reported cache deletion workaround. Done means stale cache contents cannot leave the step untransformed, and the journal assertion plus resume test succeeds.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- docker, next.js, typescript
- Domain
- build-system
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100