-
Notifications
You must be signed in to change notification settings - Fork 30.4k
Improve webpack loader error messages and handling #89698
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: canary
Are you sure you want to change the base?
Conversation
Failing test suitesCommit: 79ad382 | About building and testing Next.js
Expand output● app-dir - server-component-next-dynamic-ssr-false › should error when use dynamic ssr:false in server component
Expand output● Invalid SCSS in _document › should show a build error
Expand output● Valid and Invalid Global CSS with Custom App › should show a build error
Expand output● use-cache-unknown-cache-kind › should show a build error
Expand output● Invalid SCSS in _document › should show a build error
Expand output● use-cache-without-experimental-flag › should fail the build with an error
Expand output● app dir - prefetching › should immediately render the loading state for a dynamic segment when fetched from higher up in the tree
Expand output● react-dom/server in React Server environment › implicit react-dom/server.edge usage in app code |
CodSpeed Performance ReportMerging this PR will not alter performanceComparing Summary
Footnotes
|
Stats from current PR✅ No significant changes detected📊 All Metrics📖 Metrics GlossaryDev Server Metrics:
Build Metrics:
Change Thresholds:
⚡ Dev Server
📦 Dev Server (Webpack) (Legacy)📦 Dev Server (Webpack)
⚡ Production Builds
📦 Production Builds (Webpack) (Legacy)📦 Production Builds (Webpack)
📦 Bundle SizesBundle Sizes⚡ TurbopackClient Main Bundles: **437 kB** → **437 kB**
|
| Canary | PR | Change | |
|---|---|---|---|
| middleware-b..fest.js gzip | 756 B | 759 B | ✓ |
| Total | 756 B | 759 B |
Build Details
Build Manifests
| Canary | PR | Change | |
|---|---|---|---|
| _buildManifest.js gzip | 451 B | 452 B | ✓ |
| Total | 451 B | 452 B |
📦 Webpack
Client
Main Bundles
| Canary | PR | Change | |
|---|---|---|---|
| 5528-HASH.js gzip | 5.47 kB | N/A | - |
| 6280-HASH.js gzip | 57 kB | N/A | - |
| 6335.HASH.js gzip | 169 B | N/A | - |
| 912-HASH.js gzip | 4.53 kB | N/A | - |
| e8aec2e4-HASH.js gzip | 62.5 kB | N/A | - |
| framework-HASH.js gzip | 59.7 kB | 59.7 kB | ✓ |
| main-app-HASH.js gzip | 256 B | 254 B | ✓ |
| main-HASH.js gzip | 39.1 kB | 39.1 kB | ✓ |
| webpack-HASH.js gzip | 1.68 kB | 1.68 kB | ✓ |
| 262-HASH.js gzip | N/A | 4.53 kB | - |
| 2889.HASH.js gzip | N/A | 169 B | - |
| 5602-HASH.js gzip | N/A | 5.49 kB | - |
| 6948ada0-HASH.js gzip | N/A | 62.5 kB | - |
| 9544-HASH.js gzip | N/A | 57.6 kB | - |
| Total | 230 kB | 231 kB |
Polyfills
| Canary | PR | Change | |
|---|---|---|---|
| polyfills-HASH.js gzip | 39.4 kB | 39.4 kB | ✓ |
| Total | 39.4 kB | 39.4 kB | ✓ |
Pages
| Canary | PR | Change | |
|---|---|---|---|
| _app-HASH.js gzip | 194 B | 194 B | ✓ |
| _error-HASH.js gzip | 183 B | 180 B | 🟢 3 B (-2%) |
| css-HASH.js gzip | 331 B | 330 B | ✓ |
| dynamic-HASH.js gzip | 1.81 kB | 1.81 kB | ✓ |
| edge-ssr-HASH.js gzip | 256 B | 256 B | ✓ |
| head-HASH.js gzip | 351 B | 352 B | ✓ |
| hooks-HASH.js gzip | 384 B | 383 B | ✓ |
| image-HASH.js gzip | 580 B | 581 B | ✓ |
| index-HASH.js gzip | 260 B | 260 B | ✓ |
| link-HASH.js gzip | 2.49 kB | 2.49 kB | ✓ |
| routerDirect..HASH.js gzip | 320 B | 319 B | ✓ |
| script-HASH.js gzip | 386 B | 386 B | ✓ |
| withRouter-HASH.js gzip | 315 B | 315 B | ✓ |
| 1afbb74e6ecf..834.css gzip | 106 B | 106 B | ✓ |
| Total | 7.97 kB | 7.97 kB | ✅ -1 B |
Server
Edge SSR
| Canary | PR | Change | |
|---|---|---|---|
| edge-ssr.js gzip | 126 kB | 126 kB | ✓ |
| page.js gzip | 249 kB | 249 kB | ✓ |
| Total | 375 kB | 376 kB |
Middleware
| Canary | PR | Change | |
|---|---|---|---|
| middleware-b..fest.js gzip | 614 B | 615 B | ✓ |
| middleware-r..fest.js gzip | 156 B | 155 B | ✓ |
| middleware.js gzip | 33.3 kB | 33.2 kB | ✓ |
| edge-runtime..pack.js gzip | 842 B | 842 B | ✓ |
| Total | 34.9 kB | 34.8 kB | ✅ -158 B |
Build Details
Build Manifests
| Canary | PR | Change | |
|---|---|---|---|
| _buildManifest.js gzip | 733 B | 735 B | ✓ |
| Total | 733 B | 735 B |
Build Cache
| Canary | PR | Change | |
|---|---|---|---|
| 0.pack gzip | 3.84 MB | 3.85 MB | 🔴 +8.53 kB (+0%) |
| index.pack gzip | 103 kB | 103 kB | ✓ |
| index.pack.old gzip | 103 kB | 103 kB | ✓ |
| Total | 4.05 MB | 4.05 MB |
🔄 Shared (bundler-independent)
Runtimes
| Canary | PR | Change | |
|---|---|---|---|
| app-page-exp...dev.js gzip | 315 kB | 315 kB | ✓ |
| app-page-exp..prod.js gzip | 167 kB | 167 kB | ✓ |
| app-page-tur...dev.js gzip | 315 kB | 315 kB | ✓ |
| app-page-tur..prod.js gzip | 167 kB | 167 kB | ✓ |
| app-page-tur...dev.js gzip | 312 kB | 312 kB | ✓ |
| app-page-tur..prod.js gzip | 166 kB | 166 kB | ✓ |
| app-page.run...dev.js gzip | 312 kB | 312 kB | ✓ |
| app-page.run..prod.js gzip | 166 kB | 166 kB | ✓ |
| app-route-ex...dev.js gzip | 70.5 kB | 70.5 kB | ✓ |
| app-route-ex..prod.js gzip | 49 kB | 49 kB | ✓ |
| app-route-tu...dev.js gzip | 70.5 kB | 70.5 kB | ✓ |
| app-route-tu..prod.js gzip | 49 kB | 49 kB | ✓ |
| app-route-tu...dev.js gzip | 70.1 kB | 70.1 kB | ✓ |
| app-route-tu..prod.js gzip | 48.8 kB | 48.8 kB | ✓ |
| app-route.ru...dev.js gzip | 70.1 kB | 70.1 kB | ✓ |
| app-route.ru..prod.js gzip | 48.7 kB | 48.7 kB | ✓ |
| dist_client_...dev.js gzip | 324 B | 324 B | ✓ |
| dist_client_...dev.js gzip | 326 B | 326 B | ✓ |
| dist_client_...dev.js gzip | 318 B | 318 B | ✓ |
| dist_client_...dev.js gzip | 317 B | 317 B | ✓ |
| pages-api-tu...dev.js gzip | 43.2 kB | 43.2 kB | ✓ |
| pages-api-tu..prod.js gzip | 32.9 kB | 32.9 kB | ✓ |
| pages-api.ru...dev.js gzip | 43.2 kB | 43.2 kB | ✓ |
| pages-api.ru..prod.js gzip | 32.8 kB | 32.8 kB | ✓ |
| pages-turbo....dev.js gzip | 52.5 kB | 52.5 kB | ✓ |
| pages-turbo...prod.js gzip | 39.4 kB | 39.4 kB | ✓ |
| pages.runtim...dev.js gzip | 52.5 kB | 52.5 kB | ✓ |
| pages.runtim..prod.js gzip | 39.4 kB | 39.4 kB | ✓ |
| server.runti..prod.js gzip | 62.7 kB | 62.7 kB | ✓ |
| Total | 2.8 MB | 2.8 MB |
📝 Changed Files (4 files)
Files with changes:
app-page-exp..ntime.dev.jsapp-page-tur..ntime.dev.jsapp-page-tur..ntime.dev.jsapp-page.runtime.dev.js
View diffs
app-page-exp..ntime.dev.js
Diff too large to display
app-page-tur..ntime.dev.js
Diff too large to display
app-page-tur..ntime.dev.js
Diff too large to display
app-page.runtime.dev.js
Diff too large to display
7bc5926 to
93cd56a
Compare
93cd56a to
2bdeafb
Compare
Test that errors thrown in webpack loaders surface the source file path and error message in the CLI output. Covers four cases: - Loader throws an Error object - Loader throws a plain string (NonErrorEmittedError) - Loader has an unhandled rejected Promise - Loader has a setTimeout that throws after completion https://claude.ai/code/session_01XnHv9KVqYRDLDti1q6vni4
Add turbopack.rules config mapping each .data file to its corresponding loader, and remove the IS_TURBOPACK_TEST skip so the tests run for both webpack and Turbopack. https://claude.ai/code/session_01XnHv9KVqYRDLDti1q6vni4
Turbopack handles unhandled rejections and setTimeout errors differently from webpack when running loaders: - Unhandled promise rejections surface later attributed to a different file - setTimeout errors thrown after loader completion are swallowed entirely https://claude.ai/code/session_01XnHv9KVqYRDLDti1q6vni4
- Add loaders/error-loader path assertion for Turbopack (which preserves loader paths in stack traces) - Use loaders/ prefix for promise-error and timeout-error assertions to verify the loader path is actually present, not just matching the name in the error message - Document that string-error case loses loader path in both modes: webpack strips it via format-webpack-messages, turbopack only shows internal frames for string throws - Document that webpack strips loader path for Error throws via format-webpack-messages filtering "Module build failed (from ...)" lines https://claude.ai/code/session_01XnHv9KVqYRDLDti1q6vni4
- Preserve loader paths in format-webpack-messages.ts instead of stripping "Module build failed (from ...)" lines entirely - Add unhandledRejection handler in turbopack IPC to catch Promise.reject errors from loaders - Wrap non-Error throws (strings) with synthetic stack containing loader paths in turbopack webpack-loaders.ts - Delay loader resolution by one event loop turn to catch deferred errors (setTimeout throws, unhandled rejections) before sending IPC end message - Migrate webpack-loader-errors tests from test/development/ to test/e2e/ with proper e2e patterns (isNextDev guard, skipDeployment, error overlay) https://claude.ai/code/session_01XnHv9KVqYRDLDti1q6vni4
Instead of placing the `(from ./loader.js)` line where the original "Module build failed" header was, extract it and append at the end. This avoids shifting line positions which broke sass error detection and snapshot tests.
…Turbopack loader errors - Add no-stack-error-loader (throws Error with stack=undefined) and fs-error-loader (throws ENOENT via fs.readFileSync) test cases - Turbopack webpack-loaders.ts: append "(from <loader-path>)" to all error messages (both Error instances and string throws), matching webpack's format-webpack-messages style - Restructure tests: CLI tests for edge cases are Turbopack-only since webpack only logs errors[0] per compilation; overlay tests use generic assertions since build errors accumulate globally
Webpack error messages now include `(from .../loader.js)` attribution lines with dynamic pnpm content-addressable store hashes. Normalize these paths in test assertions and update inline snapshots to include the attribution lines with clean paths.
Filter out `(from ...)` loader attribution when the loader path is inside next/dist/. Internal loaders like next-swc-loader and sass-loader are noise for users. Third-party loader paths are still shown. Revert snapshot additions that included internal loader paths.
2bdeafb to
79ad382
Compare

What?
This PR improves error reporting for webpack loaders by:
Preserving loader paths in error messages: Modified the webpack message formatter to rewrite "Module build failed" headers to preserve the loader path information while removing the verbose prefix. This makes errors more readable in both CLI output and error overlays.
Better error wrapping for non-Error throws: When loaders throw non-Error values (strings, objects, etc.), the error is now wrapped with loader path information in the stack trace, making it easier to identify which loader caused the error.
Catching deferred loader errors: Added handling for errors that occur after loader completion (e.g., unhandled Promise rejections, setTimeout throws) by:
unhandledRejectionhandler in the IPC layerComprehensive test coverage: Added e2e tests covering various error scenarios (Error throws, string throws, Promise rejections, setTimeout errors) with verification of both CLI output and error overlay display.
Why?
Webpack loader errors were previously difficult to debug because:
How?
(from ./loaders/...)informationunhandledRejectionhandler to surface Promise-related errorsFixes #
https://claude.ai/code/session_01XnHv9KVqYRDLDti1q6vni4