You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
"This is caused by either a bug in Node.js or incorrect usage of Node.js internals. Please open an issue with this stack trace..." #35384
Platform: Linux jillian 5.8.10-arch1-1 deps: update openssl to 1.0.1j #1 SMP PREEMPT Thu, 17 Sep 2020 18:01:06 +0000 x86_64 GNU/Linux
What steps will reproduce the bug?
This occurred while sitting on 2000 concurrent https requests (using a custom agent) across 4 worker threads. ~100 of the requests hit a 2-minute timeout shortly before this happened. In short, this may be difficult to reproduce.
How often does it reproduce? Is there a required condition?
So far, it's only happened once.
Edit: I can reproduce this. 2000 concurrent requests does it, 400 does not.
What is the expected behavior?
No error messages.
What do you see instead?
In text form:
events.js:291
throw er; // Unhandled 'error' event
^
Error [ERR_INTERNAL_ASSERTION]: This is caused by either a bug in Node.js or incorrect usage of Node.js internals.
Please open an issue with this stack trace at https://github.com/nodejs/node/issues
at assert (internal/assert.js:14:11)
at JSStreamSocket.doWrite (internal/js_stream_socket.js:165:5)
at JSStream.onwrite (internal/js_stream_socket.js:33:57)
at Socket.ondata (internal/js_stream_socket.js:77:22)
at Socket.emit (events.js:314:20)
at addChunk (_stream_readable.js:307:12)
at readableAddChunk (_stream_readable.js:282:9)
at Socket.Readable.push (_stream_readable.js:221:10)
at TCP.onStreamRead (internal/stream_base_commons.js:188:23)
Emitted 'error' event on process instance at:
at emitUnhandledRejectionOrErr (internal/event_target.js:542:11)
at MessagePort.[nodejs.internal.kHybridDispatch] (internal/event_target.js:357:9)
at MessagePort.exports.emitMessage (internal/per_context/messageport.js:18:26) {
code: 'ERR_INTERNAL_ASSERTION'
}
I am also facing this issue in version 12.x (we are using AWS lambda). We are calling multiple ajax in loop and sometime this error is thrown. I had also added comment in below similar issue which is for 15.x
I ran into an assertion with the exact same stack trace the other day:
node:internal/assert:14
throw new ERR_INTERNAL_ASSERTION(message);
^
Error [ERR_INTERNAL_ASSERTION]: This is caused by either a bug in Node.js or incorrect usage of Node.js internals.
Please open an issue with this stack trace at https://github.com/nodejs/node/issues
at new NodeError (node:internal/errors:371:5)
at assert (node:internal/assert:14:11)
at JSStreamSocket.doWrite (node:internal/js_stream_socket:164:5)
at JSStream.onwrite (node:internal/js_stream_socket:33:57)
at MqttStream.ondata (node:internal/js_stream_socket:77:22)
at MqttStream.emit (node:events:526:28)
at MqttStream.emit (node:domain:475:12)
at addChunk (node:internal/streams/readable:315:12)
at readableAddChunk (node:internal/streams/readable:289:9)
at MqttStream.Readable.push (node:internal/streams/readable:228:10) {
code: 'ERR_INTERNAL_ASSERTION'
}
In my case I have a custom Duplex stream wrapped with a TLSSocket but the symptoms are the same: under moderate to high load every once in a while this assertion is hit and my app crashes.
I did some debugging and it seems to me that this is caused by a missing "current empty write" check in the native TLSWrap implementation. Debug output for the TLSSocket in question:
TLSWrap client (6653) DoWrite()
TLSWrap client (6653) Empty write
TLSWrap client (6653) Trying to read cleartext output
TLSWrap client (6653) Read -1 bytes of cleartext output
TLSWrap client (6653) No pending encrypted output, writing to underlying stream
TLSWrap client (6653) Read 33 bytes from underlying stream
TLSWrap client (6653) Trying to write cleartext input
TLSWrap client (6653) Returning from ClearIn(), no pending data
TLSWrap client (6653) Trying to read cleartext output
TLSWrap client (6653) SSLInfoCallback(SSL_CB_HANDSHAKE_START);
TLSWrap client (6653) Read -1 bytes of cleartext output
TLSWrap client (6653) Trying to write encrypted output
TLSWrap client (6653) Writing 1 buffers to the underlying stream
node:internal/assert:14
throw new ERR_INTERNAL_ASSERTION(message);
^
Error [ERR_INTERNAL_ASSERTION]: This is caused by either a bug in Node.js or incorrect usage of Node.js internals.
Please open an issue with this stack trace at https://github.com/nodejs/node/issues
To reproduce the bug the following conditions need to be fulfilled:
The TLS stack needs to trigger an "empty write" somehow. This seems to be random as far as I can tell but it happens more often around SSL handshakes or renegotiations.
The _write logic in the underlying stream needs a large enough delay before calling the callback.
At the same time additional data needs to be already present in the enc_out_ buffer so it can be erroneously written out to the underlying stream during the next EncOut pass.
I tried but haven't been able to come up with a minimal implementation that reliably reproduces the issue.
For now I am using the following workaround in JS-land, in the constructor of my TLSSocket subclass:
// Patch `JSStreamSocket` implementation to work around a bug in the native `TLSWrap` class. `TLSWrap` treats// empty writes in a special way, tracking pending ones in a separate state variable, `current_empty_write_`:// - https://github.com/nodejs/node/blob/v16.14.2/src/crypto/crypto_tls.cc#L934-L961// Sadly `TLSWrap` forgets to check this variable upon subsequent write requests, only checking `current_write_`:// - https://github.com/nodejs/node/blob/v16.14.2/src/crypto/crypto_tls.cc#L574// As a result, if another write is received before the empty write's callback is called, `TLSWrap` forwards it to// the underlying `JSStreamSocket` straight away where it then blows up with an `ERR_INTERNAL_ASSERTION`:// - https://github.com/nodejs/node/blob/v16.14.2/lib/internal/js_stream_socket.js#L164// We work around this problem by patching `doWrite` to buffer any write requests that arrive while another write// is still in progress, as well as patching `finishWrite` to execute any buffered writes after the pending empty// write has finished. This *should* prevent the assertion from being hit.// TODO: re-test with newer Node versions to see if the bug has been fixedletkCurrentWriteRequest: symbol;constjsStreamSocket=(thisasany).ssl._parentWrap;// Save original `doWrite` and `finishWrite` methodsconstdoWrite=jsStreamSocket.doWrite;constfinishWrite=jsStreamSocket.finishWrite;// List of pending write requestsconstpending: [any,Buffer[]][]=[];// Patch `doWrite` to buffer write requests if another one is already pendingjsStreamSocket.doWrite=(req: any,bufs: Buffer[])=>{// Fish the private `kCurrentWriteRequest` symbol out of the `JSStreamSocket` and cache itkCurrentWriteRequest=kCurrentWriteRequest||Object.getOwnPropertySymbols(jsStreamSocket).find(s=>s.description==="kCurrentWriteRequest");// Do we have a pending write request already?if(kCurrentWriteRequest&&jsStreamSocket[kCurrentWriteRequest]){// If so, do not do the write now, instead buffer it for laterpending.push([req,bufs]);return0;}// If not, we can go ahead with the write as usualreturndoWrite.call(jsStreamSocket,req,bufs);};// Patch `finishWrite` to dispatch any buffered write requests now that the current one has finishedjsStreamSocket.finishWrite=(handle: any,errCode: number)=>{if(handle){// Finish the current writefinishWrite.call(jsStreamSocket,handle,errCode);}// Take the next request from the buffer, if anyconstqueued=pending.shift();if(queued){// Call `doWrite` now that the previous write has finishedjsStreamSocket.doWrite(...queued);}};
With this in place I haven't been able to hit the assertion even on a ridiculously overloaded server.
I don't feel comfortable enough around C++ code to attempt to fix this myself (nor do I understand the internals of TLSWrap enough for that), but hopefully this input will help someone more knowledgeable develop a fix.
Reacted by LeadsCloud, ywave620, Ethan Witherington, Kirill Groshkov and timothyliu
Happy fourth anniversary to that issue! The latest relevant update is more than two years old, I'm going to optimistically close this, let's reopen if someone is able to reproduce with a non-EOL version of Node.js.
Activity
Trott commentedon Sep 28, 2020
@nodejs/streams @joyeecheung
mcollina commentedon Sep 28, 2020
Unfortunately it's close to impossible to help you with this without a script to reproduce this reliably.
hnviradiya commentedon Aug 30, 2021
I am also facing this issue in version 12.x (we are using AWS lambda). We are calling multiple ajax in loop and sometime this error is thrown. I had also added comment in below similar issue which is for 15.x
nodejs/help#2708
Trott commentedon Aug 30, 2021
@hnviradiya Are you able to share your Lambda code?
terreng commentedon Sep 26, 2021
mcollina commentedon Sep 26, 2021
terreng commentedon Sep 27, 2021
mcollina commentedon Sep 27, 2021
terreng commentedon Sep 27, 2021
mcollina commentedon Sep 28, 2021
terreng commentedon Sep 28, 2021
solymosi commentedon Mar 23, 2022
I ran into an assertion with the exact same stack trace the other day:
In my case I have a custom
Duplexstream wrapped with aTLSSocketbut the symptoms are the same: under moderate to high load every once in a while this assertion is hit and my app crashes.I did some debugging and it seems to me that this is caused by a missing "current empty write" check in the native
TLSWrapimplementation. Debug output for theTLSSocketin question:The failed assertion is produced as follows:
TLSWrap: https://github.com/nodejs/node/blob/v16.14.2/src/crypto/crypto_tls.cc#L944-L961current_empty_write_variable, not the regularcurrent_write_: https://github.com/nodejs/node/blob/v16.14.2/src/crypto/crypto_tls.cc#L950JSStream,JSStreamSocketand then finally to the customDuplexstream_writelogic of my customDuplexstream takes its sweet time writing the data and calls the callback with a considerable delayDuplexstream. This is forwarded intoTLSWrapwhere it triggers a call toOnStreamRead: https://github.com/nodejs/node/blob/v16.14.2/src/crypto/crypto_tls.cc#L1050OnStreamReadprocesses the read and then callsCycleat the end: https://github.com/nodejs/node/blob/v16.14.2/src/crypto/crypto_tls.cc#L1093Cyclein turn callsEncOutwhich fails to check ifcurrent_empty_write_is set – it only checkscurrent_write_: https://github.com/nodejs/node/blob/v16.14.2/src/crypto/crypto_tls.cc#L574EncOutwrites to the underlying stream even though the previous empty write is still pending: https://github.com/nodejs/node/blob/v16.14.2/src/crypto/crypto_tls.cc#L623JSStreamSocketblows up on this assertion: https://github.com/nodejs/node/blob/v16.14.2/lib/internal/js_stream_socket.js#L164To reproduce the bug the following conditions need to be fulfilled:
_writelogic in the underlying stream needs a large enough delay before calling the callback.enc_out_buffer so it can be erroneously written out to the underlying stream during the nextEncOutpass.I tried but haven't been able to come up with a minimal implementation that reliably reproduces the issue.
For now I am using the following workaround in JS-land, in the constructor of my
TLSSocketsubclass:With this in place I haven't been able to hit the assertion even on a ridiculously overloaded server.
I don't feel comfortable enough around C++ code to attempt to fix this myself (nor do I understand the internals of
TLSWrapenough for that), but hopefully this input will help someone more knowledgeable develop a fix.naveenjpr commentedon Jun 27, 2024
aduh95 commentedon Sep 28, 2024
Happy fourth anniversary to that issue! The latest relevant update is more than two years old, I'm going to optimistically close this, let's reopen if someone is able to reproduce with a non-EOL version of Node.js.