denoland/deno · error

wasm streaming callback invoked before the JS handler was se

Error message

wasm streaming callback invoked before the JS handler was set

What it means

deno_core installs an isolate-wide callback for WebAssembly streaming compilation, and its bootstrap JS registers the JS-side handler before any user code runs. If native wasm streaming starts before that handler exists — a context whose bootstrap was skipped or a snapshot built without it — the runtime panics immediately because there is nothing to deliver the stream to. The panic is a deliberate misconfiguration alarm: deno_core was set up wrong, user code is not at fault.

Source

Thrown at libs/core/ops_builtin_v8.rs:1422

}

/// The isolate-wide [`v8::Isolate::set_wasm_streaming_callback`] handler. It
/// dispatches to the JS handler registered through
/// `op_set_wasm_streaming_callback`. This is installed once at isolate creation
/// so that it survives v8 re-installing the `WebAssembly` object (and resetting
/// the isolate-wide streaming callback) on every newly created context.
pub fn wasm_streaming_callback<'a>(
  scope: &mut v8::PinScope<'a, '_>,
  arg: v8::Local<'a, v8::Value>,
  wasm_streaming: v8::WasmStreaming<false>,
) {
  let context_state_rc = JsRealm::state_from_scope(scope);
  let maybe_cb_handle = context_state_rc.js_wasm_streaming_cb.borrow().clone();
  let Some(cb_handle) = maybe_cb_handle else {
    // The JS handler is registered while the runtime bootstraps, before any
    // user code can trigger wasm streaming. Reaching this without a handler
    // means deno_core was misconfigured.
    panic!("wasm streaming callback invoked before the JS handler was set");
  };

  let streaming_rid = {
    let state = JsRuntime::state_from(scope);
    state
      .op_state
      .borrow_mut()
      .resource_table
      .add(WasmStreamingResource(RefCell::new(wasm_streaming)))
  };

  let undefined = v8::undefined(scope);
  let rid = serde_v8::to_v8(scope, streaming_rid).unwrap();
  cb_handle
    .open(scope)
    .call(scope, undefined.into(), &[arg, rid]);
}

View on GitHub (pinned to 336da420f4)

Solutions

  1. Run the standard deno_core bootstrap (which sets the wasm streaming handler) before executing any user module
  2. Rebuild custom snapshots against the current deno_core version so the handler registration is captured
  3. Audit embedder code for realms or contexts created without the runtime's init JS
  4. If this happens with an unmodified Deno CLI, report it — it would be a runtime packaging bug

Example fix

// before — user code runs before the handler exists
let runtime = JsRuntime::new(Options { ..Default::default() });
runtime.execute_script("app.js", user_code)?;

// after — bootstrap first (it registers the wasm streaming handler)
let runtime = JsRuntime::new(Options { ..Default::default() });
runtime.execute_script("deno:bootstrap", include_str!("bootstrap.js"))?;
runtime.execute_script("app.js", user_code)?;
Defensive patterns

Strategy: validation

Validate before calling

// embedder: bootstrap before any user script in every realm
runtime.execute_script("deno:bootstrap", include_str!("bootstrap.js"))?;
runtime.execute_script("app", user_code)?;

Prevention

When it happens

Trigger: An embedder creating a realm and triggering wasm streaming before running the bootstrap JS that sets js_wasm_streaming_cb; using a custom snapshot serialized before the handler was registered; harnesses executing scripts in fresh realms without the standard runtime init.

Common situations: Custom embedders with hand-rolled bootstrap ordering; stale custom snapshots after a deno_core upgrade; test setups that bypass the runtime's normal init sequence.

Related errors


AI-assisted analysis of denoland/deno@336da420f4 (2026-08-20). Data as JSON: /api/errors/ae9854b666bc32df. Report an issue: GitHub.