Skip to content

fix: queue WebSocket error/close events when closing CONNECTING socket - #4835

Closed
veeceey wants to merge 2 commits into
nodejs:mainfrom
veeceey:fix/issue-4741-websocket-close-async-events
Closed

fix: queue WebSocket error/close events when closing CONNECTING socket#4835
veeceey wants to merge 2 commits into
nodejs:mainfrom
veeceey:fix/issue-4741-websocket-close-async-events

Conversation

@veeceey

@veeceey veeceey commented Feb 23, 2026

Copy link
Copy Markdown
Contributor

When close() is called on a WebSocket that's still in the CONNECTING state, failWebsocketConnection calls handler.onSocketClose() synchronously, which fires the error and close events during the close() call itself. The spec says these should be queued as tasks and fire asynchronously after close() returns.

This wraps the onSocketClose() call in queueMicrotask() for the CONNECTING state path so events fire after close() returns, matching browser behavior.

Before:

Calling close()...
error event fired, closeReturned = false    <-- wrong
close event fired, closeReturned = false    <-- wrong

After:

Calling close()...
close() returned, closeReturned = true
error event fired, closeReturned = true     <-- correct
close event fired, closeReturned = true     <-- correct

Relevant WPTs:

  • websockets/interfaces/WebSocket/close/close-connecting.html
  • websockets/interfaces/WebSocket/close/close-connecting-async.any.js

Fixes #4741

When close() is called on a WebSocket still in the CONNECTING state,
the error and close events were firing synchronously during the close()
call. The spec requires these to be queued as tasks so they fire after
close() returns.

Wrapping the onSocketClose() call in queueMicrotask() ensures the
events fire asynchronously, matching browser behavior.

Fixes: nodejs#4741
@veeceey

veeceey commented Feb 23, 2026

Copy link
Copy Markdown
Contributor Author

Manual test results

Repro from the issue:

$ node test-repro.js
Calling close()...
close() returned, closeReturned = true
error event fired, closeReturned = true
close event fired, closeReturned = true
PASS: Events fired asynchronously after close() returned

Existing test suites pass:

  • test/websocket/close.js — 7/7 pass
  • test/websocket/events.js — 20/20 pass

@KhafraDev KhafraDev left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per domenic's feedback on nodejs#4745, the spec requires these events to be
queued as a task, not a microtask. setTimeout(fn, 0) correctly queues
a macrotask, matching the spec's "queue a task" semantics.
@Uzlopak Uzlopak closed this Feb 24, 2026
@KhafraDev

Copy link
Copy Markdown
Member

Thank you @Uzlopak

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.

WebSocket events fire synchronously during close() instead of asynchronously

3 participants