Skip to content

fix(overlay): keep a runtime error a successful build cannot vouch for - #2424

Merged
alexander-akait merged 1 commit into
mainfrom
fix/runtime-error-survives-build
Sep 26, 2026
Merged

alexander-akait merged 1 commit into
mainfrom
fix/runtime-error-survives-build

Conversation

@alexander-akait

@alexander-akait alexander-akait commented Sep 26, 2026 •

Copy link
Copy Markdown
Member

Summary

An entry that throws while it is still evaluating puts the overlay up before the transport has finished connecting. The catch-up sync arrives right behind it reporting no problems, and the reporter cleared the runtime slot on every clean build — so the overlay was dismissed milliseconds after appearing, and a page that threw on load looked fine.

This is webpack-dev-server#5024. webpack-dev-server fixes it by splitting the two cases; this package had them inverted:

webpack-dev-server webpack-dev-middleware (before)
rebuild starting DISMISS — build and runtime cleared nothing
build succeeded BUILD_OK — build only cleared build and runtime

So a runtime error was dropped by the one event that cannot vouch for it, and survived the one event that genuinely invalidates it.

Now: a build that merely succeeded clears only its own build problems, and a rebuild clears runtime errors from building — that replaces the code the error came from. Same split as webpack-dev-server, reached through this package's per-source overlay slots rather than a state machine.

What a rebuild looks like from the outside is unchanged, which is why the existing resets the runtime slot on a clean build case still passes untouched: editing a file publishes building before the rebuild, so the error still goes away — just one event earlier.

On the runtimeErrors filter through the resource query

I had flagged this as a possible second gap. It is not one: a filter round-trips intact when it is encoded in both the layers webpack-dev-server uses — encodeURIComponent on the function, then URLSearchParams on the parameter. My earlier attempt hand-wrote the query, and encodeURIComponent leaves ! alone, which webpack reads as its inline-loader separator — so the entry request was mangled before the client ever saw it. The new test encodes both layers and covers accept and reject, so this behaviour is now pinned rather than assumed.

What kind of change does this PR introduce?

fix

Did you add tests for your changes?

Yes, two end-to-end cases:

  • an entry that throws at module scope, gated on subscribeAll having handled the sync (a sync that changes nothing writes no log line, so waiting on the console would not do) — teeth-checked: restoring the old clear fails it on the selector timeout;
  • the runtimeErrors filter through the query, asserting both the error it rejects and the one it accepts, so a filter that failed to decode cannot pass by suppressing everything.

Verified locally: end-to-end 102/102, non-browser 6810 passed, lint and both typechecks clean. test/logging.test.js fails 74/74 on a clean main too, unrelated to this.

lint:types-client — which is a separate script from lint:types — caught a missing method on the ClientReporter interface in client-src/globals.d.ts; added.

Does this PR introduce a breaking change?

No. A runtime error now outlives a successful build instead of being dismissed by it; everything a rebuild does is unchanged.

If relevant, what needs to be documented once your changes are merged or what have you already documented?

Nothing — this restores expected behaviour rather than adding a knob.

Use of AI

AI-assisted (Claude Code). Used to locate the divergence against webpack-dev-server's overlay, write the fix and both tests, and verify it: the page was instrumented before any script ran to observe the overlay being raised and then wiped, and the fix was teeth-checked by restoring the old behaviour and confirming the new test fails.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA

Summary by CodeRabbit

  • Bug Fixes
    • Runtime errors remain visible in the error overlay after a successful build, rather than being dismissed when build errors are cleared.
    • Runtime errors are cleared when a rebuild begins, so the overlay reflects the latest run.
    • Configured runtime-error filters continue to determine which errors appear in the overlay.

An entry that throws while it is still evaluating puts the overlay up
before the transport has finished connecting. The catch-up sync arrives
right behind it reporting no problems, and the reporter cleared the
runtime slot on every clean build — so the overlay was dismissed
milliseconds after appearing, and a page that threw on load looked fine.
This is webpack-dev-server#5024, which webpack-dev-server fixes by
splitting the two cases; this one had not.

A build that merely succeeded says nothing about an error the page threw
by itself, so it no longer clears those. A rebuild does, from `building`,
because that replaces the code the error came from — matching
webpack-dev-server's `BUILD_OK` and `DISMISS` split. What a rebuild looks
like from the outside is unchanged.

Also covers the `runtimeErrors` filter arriving through the resource
query, which turned out to work already: encoded in both the layers
webpack-dev-server uses, a filter round-trips intact. Writing that query
by hand does not, since `encodeURIComponent` leaves `!` alone and webpack
reads it as the inline-loader separator.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
@changeset-bot

changeset-bot Bot commented Sep 26, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 899cd33

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
webpack-dev-middleware Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Sep 26, 2026

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 95b67b12-cd3d-4ef4-97fa-c5648d69e4a5

📥 Commits

Reviewing files that changed from the base of the PR and between 8bb0fdd and 899cd33.

📒 Files selected for processing (4)
  • .changeset/runtime-error-survives-build.md
  • client-src/globals.d.ts
  • client-src/index.js
  • test/e2e/overlay.test.js

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.


Walkthrough

The reporter now clears runtime problems separately from build problems. Successful builds clear build problems without dismissing runtime errors. A building message clears runtime problems. New browser tests cover runtime-error filtering and the persistence of an entry-evaluation error after catch-up sync.

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to 899cd

The overlay change is ready to merge after normal checks.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 899cd

A successful build now leaves an existing runtime error visible, as intended. A rebuild that starts but never completes can instead dismiss that error while the faulty page remains in place. No new privileged access or sensitive-data path was established.

Retained concerns

  • Low · reliability · inferred: A building event clears the page-wide runtime error before the rebuild has replaced the failing code. If rebuilding is interrupted, no subsequent transition restores that diagnostic; an unrelated compilation can trigger the same clear.
Security review details

Security Blast Radius

  • inferred — The changed clear operation affects runtime diagnostics in the browser’s shared overlay, including errors that may have originated outside the compilation reporting building; no server-side or persistent-state effect is shown.

Trust Boundaries and Controls

  • inferred — An incoming building message can dismiss a runtime diagnostic through the new path. Message-sender authority is not established by the available evidence, and the base client already permitted clean build messages to dismiss that diagnostic; a newly expanded attacker-controlled boundary is therefore unverified.

Resilience and Maintainability Implications

  • inferred — The new cleanup point can lose a failure signal during an interrupted rebuild, whereas the verified catch-up-sync behavior preserves errors on a clean sync.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: a successful build must not clear an uncaught runtime error in the overlay.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 3 files. (1 skipped: 1 …
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@alexander-akait
alexander-akait merged commit cc4946f into main Sep 26, 2026
19 checks passed
@alexander-akait
alexander-akait deleted the fix/runtime-error-survives-build branch September 26, 2026 13:21
@codecov

codecov Bot commented Sep 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.14%. Comparing base (8bb0fdd) to head (899cd33).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2424      +/-   ##
==========================================
- Coverage   96.19%   96.14%   -0.05%     
==========================================
  Files          17       17              
  Lines        1890     1894       +4     
==========================================
+ Hits         1818     1821       +3     
- Misses         72       73       +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant