Skip to content

ERR_USE_AFTER_CLOSE in repl with async eval #58784

Open
@hildjj

Description

@hildjj

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    replIssues and PRs related to the REPL subsystem.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions