rolldown/rolldown · error

Bundler is closed

Error message

Bundler is closed

What it means

After a bundler instance is closed (via close()/write/generate lifecycle completion), it can no longer be used. create_error_if_closed is a guard invoked at the start of write, generate, and scan, so any of these calls on a closed bundler throws 'Bundler is closed'.

Solutions

  1. Create a new Bundler instance for the additional build instead of reusing the closed one
  2. Reorder code so close() is only called after all write/generate/scan calls are done
  3. Use rolldown's watch API for rebuilds rather than manually re-driving a closed bundler

Example fix

// before
await bundler.generate();
await bundler.close();
await bundler.generate(); // throws
// after
await bundler.generate();
await bundler.close();
const bundler2 = await rolldown(config);
await bundler2.generate();
Defensive patterns

Strategy: try-catch

Validate before calling

if (bundler.isClosed && bundler.isClosed()) throw new Error('create a new bundler');

Type guard

const isUsable = (b) => !b.closed;

Try / catch

try { await bundler.generate(); } catch (e) { if (String(e).includes('Bundler is closed')) { bundler = await rolldown(config); await bundler.generate(); } else throw e; }

Prevention

When it happens

Trigger: Calling bundler.write(), bundler.generate(), or bundler.scan() after bundler.close() has completed, or after an implicit close from a prior completed lifecycle.

Common situations: Rebuilding with the same Bundler object after close (correct pattern is bundle.close() then a new Bundler, or watch mode); calling generate in both Node and browser contexts on a shared instance; race conditions where close finishes before a queued generate.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at crates/rolldown/src/bundler/bundler.rs:49

  ) -> BuildResult<Self> {
    let bundle_factory = BundleFactory::new(crate::BundleFactoryOptions {
      bundler_options: options,
      plugins,
      session: None,
      disable_tracing_setup: true,
    })?;

    Ok(Self {
      bundle_factory,
      session: rolldown_devtools::Session::dummy(),
      cache: ScanStageCache::default(),
      closed: false,
    })
  }

  pub(super) fn create_error_if_closed(&self) -> BuildResult<()> {
    if self.closed {
      Err(anyhow::anyhow!("Bundler is closed"))?;
    }
    Ok(())
  }

  // Implementation is split across multiple files:
  // - Normal build operations and lifecycle: `impl_bundler_build.rs`
  // - Getter/accessor methods: `impl_bundler_getter.rs`
  // - Incremental build methods: `impl_bundler_incremental_build.rs`
  // - HMR methods: `impl_bundler_hmr.rs`
}

fn _test_bundler() {
  fn assert_send(_foo: impl Send) {}
  let mut bundler = Bundler::new(BundlerOptions::default()).expect("Failed to create bundler");
  let write_fut = bundler.write();
  assert_send(write_fut);
  let mut bundler = Bundler::new(BundlerOptions::default()).expect("Failed to create bundler");
  let generate_fut = bundler.generate();

View on GitHub (pinned to 91b44b9d7b)