{"record":{"id":"14a21be24cf09f99","repo":"rolldown/rolldown","slug":"bundler-is-closed","errorCode":null,"errorMessage":"Bundler is closed","messagePattern":"Bundler is closed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/rolldown/src/bundler/bundler.rs","lineNumber":49,"sourceCode":"  ) -> BuildResult<Self> {\n    let bundle_factory = BundleFactory::new(crate::BundleFactoryOptions {\n      bundler_options: options,\n      plugins,\n      session: None,\n      disable_tracing_setup: true,\n    })?;\n\n    Ok(Self {\n      bundle_factory,\n      session: rolldown_devtools::Session::dummy(),\n      cache: ScanStageCache::default(),\n      closed: false,\n    })\n  }\n\n  pub(super) fn create_error_if_closed(&self) -> BuildResult<()> {\n    if self.closed {\n      Err(anyhow::anyhow!(\"Bundler is closed\"))?;\n    }\n    Ok(())\n  }\n\n  // Implementation is split across multiple files:\n  // - Normal build operations and lifecycle: `impl_bundler_build.rs`\n  // - Getter/accessor methods: `impl_bundler_getter.rs`\n  // - Incremental build methods: `impl_bundler_incremental_build.rs`\n  // - HMR methods: `impl_bundler_hmr.rs`\n}\n\nfn _test_bundler() {\n  fn assert_send(_foo: impl Send) {}\n  let mut bundler = Bundler::new(BundlerOptions::default()).expect(\"Failed to create bundler\");\n  let write_fut = bundler.write();\n  assert_send(write_fut);\n  let mut bundler = Bundler::new(BundlerOptions::default()).expect(\"Failed to create bundler\");\n  let generate_fut = bundler.generate();","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/rolldown/rolldown/blob/91b44b9d7bcfb5725533478e044e3891a251b2c5/crates/rolldown/src/bundler/bundler.rs#L31-L67","documentation":"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'.","triggerScenarios":"Calling bundler.write(), bundler.generate(), or bundler.scan() after bundler.close() has completed, or after an implicit close from a prior completed lifecycle.","commonSituations":"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.","solutions":["Create a new Bundler instance for the additional build instead of reusing the closed one","Reorder code so close() is only called after all write/generate/scan calls are done","Use rolldown's watch API for rebuilds rather than manually re-driving a closed bundler"],"exampleFix":"// before\nawait bundler.generate();\nawait bundler.close();\nawait bundler.generate(); // throws\n// after\nawait bundler.generate();\nawait bundler.close();\nconst bundler2 = await rolldown(config);\nawait bundler2.generate();","handlingStrategy":"try-catch","validationCode":"if (bundler.isClosed && bundler.isClosed()) throw new Error('create a new bundler');","typeGuard":"const isUsable = (b) => !b.closed;","tryCatchPattern":"try { await bundler.generate(); } catch (e) { if (String(e).includes('Bundler is closed')) { bundler = await rolldown(config); await bundler.generate(); } else throw e; }","preventionTips":["Never reuse a bundler after close(); create a new one per build","Call close() only in a final cleanup phase","Use watch mode for repeated builds instead of manual lifecycles"],"tags":["lifecycle","invalid-state","bundler"],"backgroundTag":"invalid-state-transition","analyzedSha":"91b44b9d7bcfb5725533478e044e3891a251b2c5","analyzedAt":"2026-09-07T16:04:13.504Z","contentChangedAt":"2026-09-07T16:04:13.504Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}