rolldown/rolldown · error · anyhow::Error
FileEmitter: failed to send AddEntryModule message - module…
Error message
FileEmitter: failed to send AddEntryModule message - module loader shut down during file emission
What it means
FileEmitter::emit_chunk communicates with the bundler's module loader through a channel. This error is raised when the send fails because the receiving end (the module loader task) has already been shut down, meaning file emission is being attempted after the bundling pipeline that owns emitted-file state no longer exists.
Solutions
- Emit chunks from a hook that runs while the module loader is alive (build-phase hooks such as moduleParsed / transform), not in output/generate-phase hooks
- Ensure the FileEmitter's channel receiver (module loader) is not dropped before emitting — keep the bundler task running until all emit operations complete
- If emitting an entry, use the bundler's dedicated entry-emission API at the correct lifecycle point rather than a stored FileEmitter reference
- In watch mode, guard against emitting from hooks triggered during rebuild teardown
Example fix
// before (plugin)
generateBundle() { ctx.emit_file({ type: 'chunk', fileName: 'entry.js' }); }
// after
moduleParsed() { ctx.emit_file({ type: 'chunk', fileName: 'entry.js' }); } Defensive patterns
Strategy: try-catch
Validate before calling
// TS: emit only while the build is active
if (!buildFinished) { await ctx.emitFile({ type: 'chunk', fileName: 'entry.js' }); } Try / catch
try { await ctx.emitFile(chunk) } catch (e) { if (String(e).includes('module loader shut down')) { /* emit earlier in lifecycle or re-run build */ } else { throw e } } Prevention
- Emit chunks in build-phase hooks (moduleParsed, transform), not generate/output hooks
- Never cache FileEmitter/PluginContext handles across builds
- In watch mode, stop in-flight emissions when a rebuild is triggered
- Keep the bundler promise alive until all emit calls are awaited
When it happens
Trigger: Calling emit_chunk (e.g. via an emitFile hook with a chunk/entry reference, or ctx.emit_file) after the module loader has been dropped — typically emitting a chunk during generate/output phases or from a plugin hook that runs after the build loop finished.
Common situations: A plugin emits an entry chunk in a renderStart/generateBundle-style hook instead of during build; a long-lived FileEmitter handle stored by a plugin and reused across builds or after the bundler completed; race conditions in watch mode where a rebuild tears down the loader while a hook is still emitting.
Related errors
- PluginContext: failed to send FetchModule message - module…
- A string `id` filter is not supported for the
- Bundler is closed
- Dev engine is closed
- DevEngine: coordinator closed before responding to GetState
AI-assisted analysis of rolldown/rolldown@91b44b9d7b (2026-09-07).
Data as JSON: /api/errors/2689dc731815b050.
Report an issue: GitHub.
Appendix: source
Thrown at crates/rolldown_common/src/file_emitter.rs:152
let sender = self
.tx
.lock()
.ok()
.context("Failed to acquire FileEmitter tx lock")?
.clone()
.context(
"The `PluginContext.emitFile` with `type: 'chunk'` only work at `buildStart/resolveId/load/transform/moduleParsed` hooks.",
)?;
// Only assign a reference id once we know we have a live sender — keeps
// `emit_chunk` side-effect-free on the error path.
let reference_id = self.assign_reference_id(chunk.name.clone());
sender
.send(ModuleLoaderMsg::AddEntryModule(Box::new(AddEntryModuleMsg {
chunk: Arc::clone(&chunk),
reference_id: reference_id.clone(),
})))
.map_err(|e| {
anyhow::Error::new(e).context(
"FileEmitter: failed to send AddEntryModule message - module loader shut down during file emission",
)
})?;
self.chunks.insert(reference_id.clone(), chunk);
Ok(reference_id)
}
pub fn emit_prebuilt_chunk(&self, chunk: EmittedPrebuiltChunk) -> ArcStr {
let reference_id = self.assign_reference_id(Some(chunk.file_name.clone()));
self.prebuilt_chunks.insert(reference_id.clone(), Arc::new(chunk));
reference_id
}
pub fn emit_file(
&self,
mut file: EmittedAsset,
asset_filename_template: Option<FilenameTemplate>,
sanitized_file_name: Option<ArcStr>,View on GitHub (pinned to 91b44b9d7b)