Description
Version
24.2
Platform
Darwin oz.local 24.5.0 Darwin Kernel Version 24.5.0: Tue Apr 22 19:54:49 PDT 2025; root:xnu-11417.121.6~2/RELEASE_ARM64_T6000 arm64
Subsystem
repl
What steps will reproduce the bug?
u.mjs:
import repl from 'node:repl';
const cborRepl = repl.start({
prompt: 'u> ',
eval(cmd, _context, _file, cb) {
console.log('eval', cmd);
setTimeout(() => {
cb(new Error('foo'));
}, 10);
},
});
Then at the command line: echo bar | node u.mjs
.
How often does it reproduce? Is there a required condition?
100%
What is the expected behavior? Why is that the expected behavior?
On node 24.1 and earlier, produces the follow output:
u> bar
eval bar
Uncaught Error: foo
u> ⏎
With an exit code of 0.
What do you see instead?
On node 24.2, produces:
u> bar
eval bar
Uncaught Error: foo
Uncaught Error [ERR_USE_AFTER_CLOSE]: readline was closed
at REPLServer.resume (node:internal/readline/interface:583:13)
at REPLServer.prompt (node:internal/readline/interface:434:27)
at REPLServer.displayPrompt (node:repl:1217:8)
at Domain.debugDomainError (node:repl:782:12)
at Domain.emit (node:events:507:28)
at Domain.emit (node:domain:489:12)
at finish (node:repl:961:22) {
code: 'ERR_USE_AFTER_CLOSE'
}
node:internal/readline/interface:583
throw new ERR_USE_AFTER_CLOSE('readline');
^
Error [ERR_USE_AFTER_CLOSE]: readline was closed
at REPLServer.resume (node:internal/readline/interface:583:13)
at REPLServer.prompt (node:internal/readline/interface:434:27)
at REPLServer.displayPrompt (node:repl:1217:8)
at Domain.debugDomainError (node:repl:782:12)
at Domain.emit (node:events:507:28)
at Domain.emit (node:domain:489:12)
at Domain._errorHandler (node:domain:279:23)
at Object.<anonymous> (node:domain:181:29)
at process._fatalException (node:internal/process/execution:159:29) {
code: 'ERR_USE_AFTER_CLOSE'
}
Node.js v24.2.0
With an exit code of 1.
Additional information
This was caused by #58283.
In my opinion, either:
a) the close of repl's readline instance due to the end-of-file on stdin should be delayed until any outstanding callbacks from eval have been called.
b) the callback firing after close has been called should not cause an error
There are likely several other ways to fix it.
As a quick hack of a workaround, I can add this code:
const oclose = r.close;
r.close = (...args) => {
setTimeout(() => oclose.apply(r, args), 15);
};
to move the close a little later. This is NOT RECOMMENDED, and only works in my use case because my eval callback is called pretty quickly and stays in the queue ahead of this setTimeout.