rolldown/rolldown · error

ModuleLoader: failed to send build errors - main thread…

Error message

ModuleLoader: failed to send build errors - main thread terminated while processing module errors

What it means

Like the external variant, a normal module task that fails sends `BuildErrors` to the loader main thread via a channel. The `expect` panics if the receiver is gone — the main thread terminated (panic, abort, cancellation) while the module task was still reporting its errors.

Solutions

  1. Find and fix the primary/first error in the logs — this panic is a secondary symptom
  2. Avoid cancelling the build mid-flight; let it emit its errors normally
  3. Report to rolldown if the main thread dies without emitting a primary error
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await rolldown(input);
} catch (e) {
  // treat this as secondary; find the first real module error in stderr
  console.error('primary error:', e);
}

Prevention

When it happens

Trigger: Main module-loader loop drops `rx` before all module tasks finish — caused by a fatal error elsewhere in the build, a panic in the loader, or cancellation of the build future.

Common situations: Another module's hard failure shuts down the loader while siblings are still erroring; user-initiated cancellation; plugins panicking in the main thread.

Related errors


AI-assisted analysis of rolldown/rolldown@91b44b9d7b (2026-09-07). Data as JSON: /api/errors/6ea0465beaf1b454. Report an issue: GitHub.

Appendix: source

Thrown at crates/rolldown/src/module_loader/module_task.rs:87

      module_idx: idx,
      resolved_id,
      owner,
      is_user_defined_entry,
      asserted_module_type: assert_module_type,
      flat_options,
      magic_string_tx,
    }
  }

  #[tracing::instrument(name="NormalModuleTask::run", level = "trace", skip_all, fields(module_id = %self.resolved_id.id))]
  pub async fn run(mut self) {
    if let Err(errs) = self.run_inner().await {
      self.ctx.plugin_driver.mark_context_load_modules_loaded(self.resolved_id.id.clone());
      self
        .ctx
        .tx
        .send(ModuleLoaderMsg::BuildErrors(errs.into_vec().into_boxed_slice()))
        .expect("ModuleLoader: failed to send build errors - main thread terminated while processing module errors");
    }
  }

  async fn run_inner(&mut self) -> BuildResult<()> {
    let id = self.resolved_id.id.clone();

    self.ctx.plugin_driver.set_module_info(
      &id,
      Arc::new(ModuleInfo {
        code: None,
        id: id.clone(),
        is_entry: self.is_user_defined_entry,
        importers: FxIndexSet::default(),
        dynamic_importers: FxIndexSet::default(),
        imported_ids: FxIndexSet::default(),
        dynamically_imported_ids: FxIndexSet::default(),
        exports: vec![],
        input_format: ExportsKind::None,

View on GitHub (pinned to 91b44b9d7b)