nodejs / nodejs/undici

WebSocket events fire synchronously during close() instead of asynchronously

Open
#4,741 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug websocket
Dominant language
JavaScript
Stars
7.7k
Forks
879
Avg merge
2d 16h
Merged PRs (30d)
68

Description

Bug Description

When close() is called on a WebSocket in the CONNECTING state, the error and close events fire synchronously during the close() call, rather than being queued to fire asynchronously after close() returns.

Reproducible By

const { WebSocket } = require('undici');

const ws = new WebSocket('wss://echo.websocket.events/');

let closeReturned = false;

ws.addEventListener('error', () => {
  console.log('error event fired, closeReturned =', closeReturned);
});

ws.addEventListener('close', () => {
  console.log('close event fired, closeReturned =', closeReturned);
  process.exit(0);
});

console.log('Calling close()...');
ws.close();
closeReturned = true;
console.log('close() returned, closeReturned =', closeReturned);

setTimeout(() => {
  console.log('Timeout - no events fired');
  process.exit(1);
}, 5000);

Expected Behavior

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

See: https://jsbin.com/butegexadi/edit?html,js,output

This follows from the "queue a task" steps in in the spec: https://websockets.spec.whatwg.org/#:~:text=cleanly%2C-,the%20user%20agent%20must%20queue%20a%20task

Relevant WPTs:

Logs & Screenshots

Actual behavior:

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

Environment

  • Node v25.2.1
  • undici 7.18.2

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the WebSocket.close() entry point and compare its CONNECTING-state behavior with the specification's queue-a-task requirement. Run the referenced WPTs, websockets/interfaces/WebSocket/close/close-connecting.html and close-connecting-async.any.js, and confirm that error and close events fire only after close() returns.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
networking
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
64/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.