Skip to content

WebSocket events fire synchronously during close() instead of asynchronously #4741

@domenic

Description

@domenic

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions