The Rspack Build Completed. The Import Failed.

A targeted replay found the production-only defect that turned valid default imports into undefined at runtime.

A Rspack production bundle completes before the imported package fails at runtime.

Regression Case File | Open-source historical replay

Rspack v2.1.0 shipped with a production-only failure in CommonJS default exports. Development builds behaved normally. Production builds could replace a package’s real default export with undefined, leaving consumers to fail at runtime.

We deliberately selected the known regression window and replayed the changes between v2.1.0-rc.0 and v2.1.0. The final release contained 31 commits added after the release candidate. Early returned five findings. One was classified as a Regression, and it described the real export failure.

Independent historical replay. Not affiliated with Rspack.

The Failure

The public Rspack issue #14589 reported that local-storage-fallback worked in development but failed after a production build with Rspack v2.1.0. Its default import became undefined.

The affected package used a standard Babel-compiled CommonJS shape:

exports['default'] = void 0
var _default = (exports['default'] = value)

The first assignment initializes the export. The second assignment writes the real value. In v2.1.0, production optimization could discard that second write with an unused variable declaration. The emitted bundle kept the initial undefined value.

The production bundle still completed. Development still worked. The failure appeared when a consumer used the optimized output.

Release Timeline

The release candidate, v2.1.0-rc.0, shipped on June 23, 2026 and did not contain the regression. The introducing change merged on June 24. Rspack v2.1.0 shipped on June 26, and a user reported the failure 12 minutes later. The maintainers reverted the change in v2.1.1 on June 27.

Timeline showing a working Rspack release candidate, the introducing change, the v2.1.0 failure, a user report 12 minutes later, and the v2.1.1 revert

The regression entered after the release candidate and remained in the final release for about 18 hours.

That sequence makes this a useful release-finalization case. It examines a specific decision: can a release owner comparing the approved RC with the final candidate see a harmful change before publishing the release?

How the Export Disappeared

The introducing PR #14260 changed how rspack_plugin_javascript reasons about side effects in CommonJS export assignments.

The optimization recognized the right-hand side as pure. It then treated the surrounding variable declaration as removable because the assigned variable was unused. That conclusion missed a separate effect inside the same expression: assigning the value to exports['default'].

A Babel CommonJS assignment remains in the release candidate but is removed during v2.1.0 production optimization, leaving the default export undefined

Removing the unused declaration also removed the export write that consumers needed.

This was not a generic CommonJS failure. The reproduction required the chained assignment form shown above. A plain later reassignment did not trigger it. The failure also depended on production optimization, which explains why development could remain healthy.

The Historical Replay

The replay compared the rspack_plugin_javascript code in v2.1.0-rc.0 with v2.1.0. The stored run record lists seven changed files from that plugin, including the three source files changed by the introducing pull request.

Early returned five findings from the comparison:

VerdictCountIndependent review
Regression1The real CommonJS export defect
Expected4Not all independently verified as harmless

The precision statement is deliberately narrow. The only Regression finding matched the known defect. There were no unmatched Regression findings. We did not independently establish that every Expected finding was harmless.

What Early Found

The finding was titled “CJS export-assign now DCE-prunable via declarator.” It identified the main culprit file and the newly added branch that treated the chained assignment as pure. It then followed the effect into production output: the export assignment could disappear and exports.default could remain at its initial undefined value.

The user-visible result was:

Early finding

“CJS export-assign now DCE-prunable via declarator”

Verdict: Regression

Early product output showing the CommonJS export assignment classified as a Regression beside Expected changes from the same rspack comparison

Actual Early product output from the historical replay. The finding and verdict are transcribed above for mobile readability.

The finding also proposed a concrete verification. Build a module with an observable load effect, reference it through an unused top-level CommonJS assignment, run a production build, and inspect both the emitted chunk and runtime output.

That stored procedure exercised the same faulty optimization path through a sibling CommonJS assignment. Our independent reproduction used the upstream Babel assignment shape and reached the same failure branch.

Verification and Limits

We ran an npm-only reproduction across four versions. Every production build command exited successfully.

The discriminating probe used the issue’s code in .js files. In our environment, the literal .cjs and .mjs file layout from the report returned a namespace object in every tested version, so it could not distinguish the regression boundary. The .js probe preserved the chained Babel assignment that triggered the faulty optimization and produced the clean version boundary below.

VersionResultRuntime output
v2.0.8PassRESULT:42
v2.1.0-rc.0PassRESULT:42
v2.1.0FailRESULT:undefined
v2.1.1PassRESULT:42

The clean boundary independently confirms the introducing and fixed releases. The v2.1.1 fix was a pure revert of PR #14260.

The introducing change merged after normal review activity and automated checks. Three reviewers commented, none recorded a formal approval, and two of 52 check runs failed. This case does not support a claim that the change passed every check. It shows that the production export failure remained in the final release despite the review and automation that took place.

The replay happened after the incident, and researchers selected the window because it contained a known regression. It is a case study, not an estimate of detection performance across arbitrary releases. Early did not participate in the original release, and this replay does not show that Early prevented it.

The supported conclusion is narrower. In the 31-commit comparison between the release candidate and final release, Early’s only Regression finding identified the real production export failure, its source mechanism, and a procedure for verifying the effect.

Reproduce and Verify

The case materials, including the release history, product output, and npm-only reproduction, are available by request. Contact us to request access.

With the materials, you can:

  1. Inspect the code and Git history between v2.1.0-rc.0 and v2.1.0.
  2. Compare Early’s finding with the introducing change and later upstream revert.
  3. Run the four-version reproduction and verify the pass, pass, fail, pass boundary.

The reproduction independently tests the runtime failure and fix. It does not rerun Regression Guard or recreate its proprietary analysis.

Sources and Method

Primary public sources include the user report, the RC-to-release comparison, the introducing pull request, the revert, and the fixed release comparison.

The historical replay ran on August 22, 2026. Human verification compared the product output with the release code, the executed reproduction, and the later upstream record.

Related articles

A Pydantic AI Regression Inside a 47-File FixA targeted historical replay traced incomplete streaming responses to one drain loop removed during a 47-file cleanup.A Hono Form Parsing Regression Inside a Small FixA targeted replay found one real form parsing regression among eight findings in Hono v4.12.28.Which Change Caused the Incident?The better question is whether we could have caught the regression before production.

Remember your last regression?