denoland/deno · critical

web workers are not supported

Error message

web workers are not supported

What it means

runtime/worker.rs:322 is the default create_web_worker_cb inside WorkerOptions::default(); it is an unimplemented!() panic that fires only if a runtime embedder never supplied a real web-worker factory. The Deno CLI always overrides this callback, so end users should never see it — seeing it means a custom deno_runtime/deno_core integration started a JS Worker (new Worker(...)) without wiring nested-worker creation.

Source

Thrown at runtime/worker.rs:322

  // user code.
  pub should_wait_for_inspector_session: bool,
  /// If Some, print a low-level trace output for ops matching the given patterns.
  pub trace_ops: Option<Vec<String>>,

  pub cache_storage_dir: Option<std::path::PathBuf>,
  pub origin_storage_dir: Option<std::path::PathBuf>,
  pub stdio: Stdio,
  pub enable_raw_imports: bool,
  pub enable_stack_trace_arg_in_ops: bool,

  pub unconfigured_runtime: Option<UnconfiguredRuntime>,
}

impl Default for WorkerOptions {
  fn default() -> Self {
    Self {
      create_web_worker_cb: Arc::new(|_| {
        unimplemented!("web workers are not supported")
      }),
      skip_op_registration: false,
      seed: None,
      unsafely_ignore_certificate_errors: Default::default(),
      should_break_on_first_statement: Default::default(),
      should_wait_for_inspector_session: Default::default(),
      trace_ops: Default::default(),
      format_js_error_fn: Default::default(),
      origin_storage_dir: Default::default(),
      cache_storage_dir: Default::default(),
      extensions: Default::default(),
      startup_snapshot: Default::default(),
      residual_lazy_js_sources: &[],
      residual_lazy_esm_sources: &[],
      create_params: Default::default(),
      bootstrap: Default::default(),
      stdio: Default::default(),
      enable_raw_imports: false,

View on GitHub (pinned to 89f33cbef2)

Solutions

  1. Provide a real create_web_worker_cb when constructing WorkerOptions (Arc<dyn Fn(WebWorkerOptions) -> Result<WebWorker>>), typically reusing deno_runtime::worker::create_web_worker_init_cb and re-registering your extensions
  2. Decide the embedding policy: disable worker creation entirely by erroring deliberately in the callback instead of leaving the unimplemented!() default
  3. If you intended the Deno CLI behavior, run scripts through the deno binary rather than a hand-rolled runtime bootstrap

Example fix

// before (Rust embedder)
let options = WorkerOptions::default(); // create_web_worker_cb = unimplemented!

// after
let options = WorkerOptions {
  create_web_worker_cb: Arc::new(|init| {
    Ok(deno_runtime::worker::WebWorker::bootstrap_from_options(
      init.main_module,
      deno_runtime::worker::WebWorkerOptions { /* extensions, perms, ... */ ..init },
    ))
  }),
  ..Default::default()
};
Defensive patterns

Strategy: validation

Validate before calling

// Rust, before creating the runtime
fn assert_workers_supported(options: &WorkerOptions) -> Result<(), String> {
    let ok = matches!(
        std::thread::catch_unwind(std::panic::AssertUnwindSafe(|| {
            // probing is unsafe; instead track whether the cb was set by construction
            false
        })),
        _ => false
    );
    let _ = ok;
    // Practical check: gate on your own builder flag
    if !options.extensions.is_empty() /* proxy for custom bootstrap */ {
        Ok(())
    } else {
        Err("create_web_worker_cb not supplied".into())
    }
}

Try / catch

// JS side: you cannot catch a Rust unimplemented!() panic — prevent it in the embedder instead.
// Rust embedder: wrap runtime execution in catch_unwind to log which feature requested a Worker.
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
    rt.block_on(main_js_future)
}));
if result.is_err() { /* log 'web workers requested but unsupported' */ }

Prevention

When it happens

Trigger: Embedding deno_runtime with WorkerOptions::default() (or any Options struct where create_web_worker_cb was never set) and then executing JavaScript that calls new Worker(new URL("./w.js", import.meta.url), { type: "module" }) or new SharedWorker.

Common situations: Building custom runtimes/sandboxes on deno_core+deno_runtime (server-side scripting hosts, plugin systems, edge platforms); copying a minimal deno_runtime example that predates the callback being required; upgrading deno_runtime versions where defaults became stricter about nested workers.

Related errors


AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16). Data as JSON: /api/errors/8898e4c02193c73c. Report an issue: GitHub.