denoland/deno · error · ERR_INVALID_REPL_INPUT
ERR_INVALID_REPL_INPUT
ERR_INVALID_REPL_INPUT
Error message
Listeners for `uncaughtException` cannot be used in the REPL
What it means
Node's REPL installs its own uncaughtException handler so an error in evaluated code does not kill the interactive session. To keep that handler from being shadowed, Node (and Deno's node:repl polyfill) reject any additional `uncaughtException` listener registered while a non-standalone REPL is active. The polyfill enforces this with a refcounted `newListener` guard that only accepts functions named `_replUncaughtHandler`, so the offending `process.on()` call throws ERR_INVALID_REPL_INPUT synchronously.
Source
Thrown at ext/node/polyfills/repl.ts:184
p.setUncaughtExceptionCaptureCallback(null);
if (typeof _prevCaptureCallback === "function") {
p.setUncaughtExceptionCaptureCallback(_prevCaptureCallback);
}
_prevCaptureCallback = null;
}
// Node's REPL refuses user-installed `uncaughtException` listeners so the
// REPL's own handler isn't shadowed. Mirror that with a counted
// `newListener` guard installed once and removed when the last non-
// standalone REPL exits. The REPL's own handler is named
// `_replUncaughtHandler`, matching the only allowed function name.
let _newListenerGuardCount = 0;
function _newListenerGuard(event: string, listener: any) {
if (
event === "uncaughtException" &&
listener && listener.name !== "_replUncaughtHandler"
) {
throw new ERR_INVALID_REPL_INPUT(
"Listeners for `uncaughtException` cannot be used in the REPL",
);
}
}
function _addNewListenerGuard() {
if (_newListenerGuardCount++ === 0) {
process.prependListener("newListener", _newListenerGuard);
}
}
function _removeNewListenerGuard() {
if (--_newListenerGuardCount === 0) {
process.removeListener("newListener", _newListenerGuard);
}
}
const reAtFrame = new SafeRegExp(/^\s+at\s/);
const reExtFrame = new SafeRegExp(/\bext:[a-z_]+\//);
const reNodeParenFrame = new SafeRegExp(/\(node:[a-z_]+:/);View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Move the `process.on('uncaughtException', ...)` registration out of REPL-evaluated code into the entry-point script
- Run the code as a file (`deno run app.ts`) instead of pasting it into the REPL
- If you must stay interactive, wrap the risky section in try/catch instead of registering a global handler
- As a discouraged last resort, name the handler `_replUncaughtHandler` — it bypasses the guard but shadows the REPL's handler
Example fix
// before (typed in the REPL)
process.on('uncaughtException', (e) => console.error(e));
// ERR_INVALID_REPL_INPUT
// after (in the REPL)
try { risky(); } catch (e) { console.error(e); }
// or: put process.on('uncaughtException', ...) in the script and run `deno run app.ts` Defensive patterns
Strategy: try-catch
Try / catch
try {
process.on('uncaughtException', handler);
} catch (e) {
if (e.code === 'ERR_INVALID_REPL_INPUT') {
// Running inside a REPL: fall back to local try/catch handling
} else {
throw e;
}
} Prevention
- Register global crash handlers only in entry-point scripts, never in REPL-loaded code
- Use try/catch for exploratory error handling in a REPL
- Detect REPL context at runtime by catching code ERR_INVALID_REPL_INPUT
When it happens
Trigger: Running `process.on('uncaughtException', handler)` (or addListener/prependListener) inside a `deno` REPL session, or inside code evaluated by a non-standalone `repl.start()` — any listener whose function name is not `_replUncaughtHandler` triggers the throw.
Common situations: Pasting a script that installs a global crash handler into the REPL to debug it; `.load`ing a test-setup file that registers uncaughtException listeners; migrating CLI tools that assume they run as plain scripts rather than inside a REPL.
Related errors
- ERR_INVALID_ARG_TYPE
- Cannot convert a Symbol value to a string
- ERR_INVALID_OBJECT_DEFINE_PROPERTY
- ERR_WORKER_UNSUPPORTED_OPERATION
- ERR_INVALID_ARG_VALUE
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/7acef922a6ef2b67.
Report an issue: GitHub.