denoland/deno · critical
JS PANIC: {}
Error message
JS PANIC: {} What it means
op_panic is deno_core's escape hatch for JS runtime internals: when internal bootstrap/extension JS reaches an impossible state, it calls this op, which prints `JS PANIC: <message>` to stderr and panics the runtime, taking down the process. The message names the internal invariant that failed. It is unrelated to user exceptions — uncaught user errors go through the normal error path.
Source
Thrown at libs/core/ops_builtin.rs:155
ops_builtin_v8::op_op_names,
ops_builtin_v8::op_current_user_call_site,
ops_builtin_v8::op_set_format_exception_callback,
ops_builtin_v8::op_event_loop_has_more_work,
ops_builtin_v8::op_immediate_check,
ops_builtin_v8::op_leak_tracing_enable,
ops_builtin_v8::op_leak_tracing_submit,
ops_builtin_v8::op_leak_tracing_get_all,
ops_builtin_v8::op_leak_tracing_get,
ops_builtin_v8::op_get_ext_import_meta_proto
}
#[op2(fast)]
pub fn op_panic(#[string] message: String) {
#[allow(clippy::print_stderr, reason = "intentional panic output")]
{
eprintln!("JS PANIC: {}", message);
}
panic!("JS PANIC: {}", message);
}
/// Return map of resources with id as key
/// and string representation as value.
#[op2]
pub fn op_resources(state: &mut OpState) -> Vec<(ResourceId, String)> {
state
.resource_table
.names()
.map(|(rid, name)| (rid, name.to_string()))
.collect()
}
#[op2(fast)]
fn op_add(a: i32, b: i32) -> i32 {
a + b
}
View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Reinstall/update Deno so the binary, snapshot, and JS runtime all come from the same version
- Read the message — it names the failing invariant, which maps to a specific runtime subsystem
- If your own code reaches for core.ops.op_panic, throw a normal Error instead
- Report with the exact JS PANIC message and a reproducer when it comes from Deno internals
Example fix
// before — extension code invokes the internal panic op
Deno.core.ops.op_panic("unreachable branch");
// after — throw a catchable JS error
throw new Error("unreachable branch"); Defensive patterns
Strategy: fallback
Prevention
- Keep the Deno binary, snapshot, and JS runtime from the same version — reinstall cleanly on upgrade mismatches
- Never call Deno.core.ops.op_panic from your own code; throw a normal Error
- Capture the exact JS PANIC message when reporting — it names the broken invariant
When it happens
Trigger: Internal Deno JS code (runtime bootstrap, core extensions) signaling an unreachable state, or embedder/extension code deliberately calling Deno.core.ops.op_panic. The panic is process-fatal and cannot be caught from JS.
Common situations: Mismatched runtime pieces after a partial Deno upgrade or corrupted install (binary vs snapshot vs JS); snapshots built from a different deno_core version; genuine bugs in Deno internals on unusual code paths.
Related errors
- ${name} is already registered
- Attempted to read snapshot data out of range: {id} (of {})
- Attempted to read the snapshot data at index {id} twice
- Invalid data type at index {id}, expected '{}'
- {path} exists
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/e424ac2863b7974a.
Report an issue: GitHub.