fix(dev): forcibly close websocket connections when quitting - #1532
Conversation
commit: |
CLI benchmark
Full report
|
| Setting | Value |
|---|---|
| Baseline | ref:7415659f139227d558a5904b930fa7b493104e30 (v4.0.0-alpha.1) |
| Head | local packages/nuxt-cli at c618148 (v4.0.0-alpha.1) |
| Node | v24.21.0 |
| OS | Linux 6.17.0 (kernel 6.17.0-1022-azure) |
| CPU | AMD EPYC 7763 64-Core Processor x 4 |
| Memory | 15.6 GB |
| Load average at start | 0.78, 0.23, 0.08 |
| Run started | 2026-09-26T13:05:58.505Z |
Cold CLI startup
Median of 15 interleaved runs per command, one warmup discarded.
| Command | baseline v4.0.0-alpha.1 median | head v4.0.0-alpha.1 median | Delta | baseline v4.0.0-alpha.1 min / p95 | head v4.0.0-alpha.1 min / p95 |
|---|---|---|---|---|---|
nuxt --version |
71 ms | 70 ms | -0.7% | 69 ms / 74 ms | 67 ms / 73 ms |
nuxt --version (first output byte) |
66 ms | 66 ms | -0.8% | 64 ms / 69 ms | 62 ms / 69 ms |
nuxt --help |
156 ms | 156 ms | -0.5% | 152 ms / 161 ms | 152 ms / 163 ms |
nuxt --help (first output byte) |
151 ms | 150 ms | -0.6% | 146 ms / 155 ms | 146 ms / 157 ms |
nuxt dev --help |
111 ms | 112 ms | +1.2% | 107 ms / 117 ms | 108 ms / 125 ms |
nuxt dev --help (first output byte) |
105 ms | 107 ms | +1.6% | 101 ms / 111 ms | 103 ms / 120 ms |
nuxt <unknown-command> (no-op) |
168 ms | 168 ms | +0.1% | 163 ms / 177 ms | 162 ms / 174 ms |
nuxt <unknown-command> (no-op) (first output byte) |
161 ms | 162 ms | +0.5% | 157 ms / 171 ms | 156 ms / 168 ms |
Module load cost
Counted with a module.registerHooks load hook, compile cache disabled. Counts every JS module actually evaluated on that code path (built-ins excluded, native addons excluded).
| Command | baseline v4.0.0-alpha.1 modules | head v4.0.0-alpha.1 modules | Delta | baseline v4.0.0-alpha.1 source bytes | head v4.0.0-alpha.1 source bytes | Delta |
|---|---|---|---|---|---|---|
nuxt --version |
38 | 38 | 0.0% | 296.7 kB | 296.7 kB | 0.0% |
nuxt --help |
144 | 144 | 0.0% | 958.9 kB | 958.9 kB | 0.0% |
nuxt dev --help |
63 | 63 | 0.0% | 449.8 kB | 449.8 kB | 0.0% |
Install footprint and published tarball
Each version installed on its own into an empty project with nothing but @nuxt/cli as a dependency, so the tree is exactly the CLI and its transitive dependencies. npm cache is warm and the registry is only consulted for metadata, so install wall time is indicative, not a network benchmark.
| Metric | baseline v4.0.0-alpha.1 | head v4.0.0-alpha.1 | Delta |
|---|---|---|---|
Direct dependencies of @nuxt/cli |
22 | 22 | 0.0% |
| Packages in the installed tree (unique name@version) | 38 | 38 | 0.0% |
| Unique package names | 38 | 38 | 0.0% |
| Package directories on disk (cross-check) | 31 | 31 | 0.0% |
Installed node_modules on disk |
2.39 MB | 2.39 MB | +0.0% |
| Installed files | 420 | 420 | 0.0% |
| Install wall time (warm npm cache, median of 3) | 1.37 s | 1.38 s | +1.1% |
| Published tarball (packed) | 236.0 kB | 236.0 kB | +0.0% |
| Published tarball (unpacked) | 769.0 kB | 769.0 kB | +0.0% |
| Files in tarball | 97 | 97 | 0.0% |
Interleaved runs on a shared runner: trust the deltas, not the absolute timings. The dev, restart and build suites run locally via pnpm bench:cli.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1532 +/- ##
=======================================
Coverage ? 83.39%
=======================================
Files ? 166
Lines ? 10944
Branches ? 3170
=======================================
Hits ? 9127
Misses ? 1543
Partials ? 274 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Nuxt cli timeout: there is a bug at nitropack (nitro v2.13.4), when closing the worker, if there is a timeout, the caller will hang forever, in this case, nuxt cli will timeout with its default (15 seconds), missing The Patching nitropack on my local node_modules calling the |
|
Found the issue, it is a dead lock with nitropack 🥳 🥳 🥳 |
The dead lockWhen shutting down the dev server (nitro dev-server at nitropack), the process often delays for 5 seconds until it throws Root CauseThere is a deadlock between the dev worker and the parent proxy during shutdown.
This results in an infinite loop that only breaks when the parent's 5-second graceful shutdown timeout forcefully terminates the worker thread. The Fix at nitropackBy calling Changes at nitro-dev.ts shutdown async function shutdown() {
server.closeAllConnections?.();
listener?.close();
await nitroApp.hooks.callHook("close").catch(console.error);
parentPort?.postMessage({ event: "exit" });
}Applied these changes on my local: async function shutdown() {
console.log('🛑 [Worker] Shutdown started...');
server.closeAllConnections?.();
listener?.close();
await nitroApp.hooks.callHook("close").then(() => console.log('🛑 [Worker] Nitro hooks called')).catch(console.error);
console.log('🛑 [Worker] Sending "exit" event to the parent...');
parentPort?.postMessage({ event: "exit" });
}
|
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. Warning Review limit reachedNext included review available in 1 minute. View limit detailsLimit details: You’ve used all 2 included reviews currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (2)
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (2)
Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 1 remain after this review. 📝 WalkthroughWalkthroughThe development server now tracks each WebSocket immediately after acceptance. The tracking occurs before Vite HMR filtering and Nitro upgrade handling. Each socket is removed from the tracking set when it closes, so shutdown cleanup can destroy connections that bypass request handling. Priority: ➖ Normal Estimated code review effort: 2 (Simple) | ~10 minutes Severity of issue fixed: Medium Merge Risk: ⚪ Minimal · up to The change closes tracked upgraded connections before Nuxt shuts down, and the lifecycle test exercises that sequence. No concrete merge-blocking risk remains in the reviewed scope. Security Architecture ReviewSecurity architecture risk: 🔵 Low · up to The change closes tracked development-server connections earlier to prevent shutdown hangs. Review found no new network entrypoint or weakened upgrade control, though concurrent shutdown behavior is not directly tested. Retained concerns Security review detailsSecurity Blast Radius
Trust Boundaries and Controls
Resilience and Maintainability Implications
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
this seems like a nitro bug, rather than something that should be worked around here cc: @pi0 |



🔗 Linked issue
resolves #1531
📚 Description
This PR includes:
Maybe we can fix the deadlock here, not yet sure...