stream: fix writev unhandled rejection in fromWeb#62297
stream: fix writev unhandled rejection in fromWeb#62297Han5991 wants to merge 1 commit intonodejs:mainfrom
Conversation
|
#62291 is already open (although I believe the approach here is the correct one) |
|
Agreed — and since #62291 is already open, I've opened this as a separate issue because I believe the approach here is the correct one. @Renegade334 |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #62297 +/- ##
==========================================
- Coverage 89.72% 89.71% -0.01%
==========================================
Files 695 695
Lines 214464 214462 -2
Branches 41067 41059 -8
==========================================
- Hits 192420 192410 -10
Misses 14106 14106
- Partials 7938 7946 +8
🚀 New features to boost your workflow:
|
|
Could you possibly review this PR once again? I am aware that there is a previous PR, but it seems quite some time has passed. |
1af4a4a to
7ce5532
Compare
When using Duplex.fromWeb() or Writable.fromWeb() with cork()/uncork(), writes are batched into _writev(). If destroy() is called in the same microtask, the underlying WritableStream writer gets aborted, causing SafePromiseAll() to reject with a non-array value (e.g. an AbortError). The done() callback in _writev() of both fromWeb adapter functions unconditionally called error.filter(), assuming the value was always an array. This caused a TypeError that became an unhandled rejection, crashing the process. Fix by separating the resolve and reject handlers of SafePromiseAll: use () => done() on the resolve path (all writes succeeded, no error) and done on the reject path (error passed directly to callback). Fixes: nodejs#62199 Signed-off-by: sangwook <rewq5991@gmail.com>
7ce5532 to
5df0476
Compare
|
#62297 Jenkins failures appear unrelated to this PR. The failing test across platforms is test-inspector-dom-storage, and the stack trace points to node:internal/inspector/webstorage / node:inspector, not lib/internal/webstreams/adapters.js. The failure matches a separate main regression introduced by d93935b (inspector: auto collect webstorage data, PR #62145), where DOMStorage events were emitted without coercing key/value to strings. That regression was then fixed by 449a93a (inspector: coerce key and value to string in webstorage events, PR #62616). This Jenkins run appears to have picked up the broken main state before the follow-up fix landed, rather than a problem caused by #62297. Could CI be re-run on the current base? |
Summary
Fixes #62199
When using
Duplex.fromWeb()orWritable.fromWeb()withcork()/uncork(), writes are batched into_writev(). Ifdestroy()is called in the same microtask (afteruncork()), the underlyingWritableStreamwriter gets aborted, causingSafePromiseAll()to reject with a non-array value (e.g. anAbortErrorornull).The
done()callback in_writev()of bothnewStreamDuplexFromReadableWritablePairandnewStreamWritableFromWritableStreamunconditionally callederror.filter(), assuming the value was always an array fromSafePromiseAll. This caused aTypeError: Cannot read properties of null (reading 'filter')that became an unhandled rejection, crashing the process.Root cause
donewas used as both the resolve and reject handler forSafePromiseAll:done([undefined, undefined, ...])— array,.filter()worksdone(abortError)ordone(null)— non-array,.filter()throwsFix
Separate the resolve and reject handlers of
SafePromiseAll.Promise.allresolving means all writes succeeded — there is no error to report.Promise.allrejecting passes a single error value directly.() => done()— resolve path: all writes succeeded, call callback with no errordone— reject path: single error passed directly to callbackThe same fix is applied to both
newStreamDuplexFromReadableWritablePairandnewStreamWritableFromWritableStream.Test
Added
test/parallel/test-webstreams-duplex-fromweb-writev-unhandled-rejection.jswith the exact reproduction from the issue report (cork → write → write → uncork → destroy).