Skip to content

Signal listeners added by process.once cannot be removed by process.removeListener #32603

@Macil

Description

@Macil

Version: Deno 2.7.4

Signal event listeners added through process.once cannot be removed by process.removeListener in Deno. This works in Node. Example code:

import process from "node:process";

function sigintHandler() {
  console.log("SIGINT caught");
}

process.once("SIGINT", sigintHandler);
process.removeListener("SIGINT", sigintHandler);

console.log("SIGINT listeners:", process.listenerCount("SIGINT"));

process.kill(process.pid, "SIGINT");

// Wait a bit to ensure the signal is processed before the script exits
setTimeout(() => {
  console.log("Ran to end");
}, 2_000);
$ node processOnceExample.ts
SIGINT listeners: 0
Emitting SIGINT...

$ deno run --allow-run processOnceExample.ts
SIGINT listeners: 0
Emitting SIGINT...
SIGINT caught
Ran to end

Deno's implementation of the node:events EventEmitter handles once + removeListener fine, but Deno's implementation of process extends EventEmitter while overriding the on + removeListener methods without making them handle the wrapping of the listener that EventEmitter.once does (and that EventEmitter.prependOnceListener does), so the later call to removeListener sees the unwrapped listener and can't match it to the once-wrapped listener.

This bug caused a compatibility issue for me with the listr2 npm package, which adds a SIGINT handler using once while it's running and then removes it afterward. Because of the bug in Deno, the SIGINT handler never gets removed, and that handler kills the process on SIGINT, preventing any future SIGINT handlers from implementing graceful shutdown.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions