{"record":{"id":"78368177cd1e34ca","repo":"rolldown/rolldown","slug":"dynamic-entry-module-target-should-have-a-wrapper","errorCode":null,"errorMessage":"dynamic-entry module target should have a wrapper","messagePattern":"dynamic-entry module target should have a wrapper","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/rolldown/src/stages/generate_stage/compute_cross_chunk_links.rs","lineNumber":1361,"sourceCode":"            }\n            let meta = &self.link_output.metas[*dynamic_entry_module];\n            if matches!(meta.wrap_kind(), WrapKind::Cjs) {\n              // For CJS modules, export only wrapper_ref (require_xxx)\n              // Generated code: `import('./chunk.js').then((n) => __toESM(n.require_xxx()))`\n              if let Some(wrapper_ref) = meta.wrapper_ref {\n                index_chunk_exported_symbols[chunk_id].entry(wrapper_ref).or_default();\n              }\n            } else if let Some(targets) =\n              order_state.consumer_local_namespace_targets(*dynamic_entry_module)\n            {\n              // A consumer-local namespace is activated by its complete leaf/carrier target list,\n              // never by the intentionally empty shared barrel wrapper.\n              for &target in targets {\n                let wrapper_ref = match target {\n                  WrappedEsmInitTarget::Module(module_idx) => {\n                    order_state\n                      .esm_init_target(module_idx, &self.link_output.metas[module_idx])\n                      .expect(\"dynamic-entry module target should have a wrapper\")\n                      .wrapper_ref\n                  }\n                  WrappedEsmInitTarget::CjsCarrier(key) => {\n                    order_state\n                      .order_cjs_carrier(key)\n                      .expect(\"dynamic-entry CJS carrier should have a wrapper\")\n                      .wrapper_ref\n                  }\n                };\n                index_chunk_exported_symbols[chunk_id].entry(wrapper_ref).or_default();\n              }\n              let ns_ref = self.link_output.module_table[*dynamic_entry_module]\n                .namespace_object_ref()\n                .expect(\"dynamic entry should be normal module\");\n              index_chunk_exported_symbols[chunk_id].entry(ns_ref).or_default();\n            } else if let Some(target) = order_state.esm_init_target(*dynamic_entry_module, meta) {\n              // For ESM modules, export both wrapper_ref (init_xxx) and namespace\n              // Generated code: `import('./chunk.js').then((n) => (n.init_xxx(), n.namespace))`","sourceCodeStart":1343,"sourceCodeEnd":1379,"githubUrl":"https://github.com/rolldown/rolldown/blob/91b44b9d7bcfb5725533478e044e3891a251b2c5/crates/rolldown/src/stages/generate_stage/compute_cross_chunk_links.rs#L1343-L1379","documentation":"Rolldown panics with 'dynamic-entry module target should have a wrapper' while computing chunk imports. For a dynamic entry's ESM init target, `order_state.esm_init_target(module_idx, meta)` must yield a target carrying a `wrapper_ref` (the symbol that lazily initializes the dynamic entry's ESM namespace). The panic means an init target was recorded in the order state without its wrapper being created — order-wrap metadata is incomplete when the cross-chunk import graph is built.","triggerScenarios":"In `compute_chunk_imports` (from `compute_cross_chunk_link_state`): iterating `consumer_local_namespace_targets`, a `WrappedEsmInitTarget::Module(module_idx)` resolves via `esm_init_target(...)` to `None`, or the companion `CjsCarrier` lookup misses. Triggered by dynamic `import()` entries participating in consumer-local re-export routes under code splitting — an internal bookkeeping bug.","commonSituations":"Hit with apps that dynamically import entry points re-exported through barrels (consumer-local namespace targets), typically under experimental order-wrap/code-splitting flags after a rolldown upgrade that altered when wrappers are created for dynamic entries.","solutions":["Upgrade rolldown to the latest version; wrapper-creation gaps for dynamic entries are patched upstream.","Workaround: avoid dynamic-importing a barrel/re-export module directly — import a concrete module instead.","Temporarily disable consumer-local re-export / order-wrap experimental options if enabled.","Contributors: compare where `esm_init_target`s are registered in the order pass against where `wrapper_ref` is assigned to find the missing path."],"exampleFix":"// before (panic)\nlet wrapper_ref = match target {\n  WrappedEsmInitTarget::Module(module_idx) => {\n    order_state\n      .esm_init_target(module_idx, &self.link_output.metas[module_idx])\n      .expect(\"dynamic-entry module target should have a wrapper\")\n      .wrapper_ref\n  }\n  // ...\n};\n// after (defensive library fix)\nlet wrapper_ref = match target {\n  WrappedEsmInitTarget::Module(module_idx) => {\n    match order_state.esm_init_target(module_idx, &self.link_output.metas[module_idx]) {\n      Some(t) => t.wrapper_ref,\n      None => continue,\n    }\n  }\n  // ...\n};","handlingStrategy":"try-catch","validationCode":"// Prefer dynamic-importing concrete modules over barrels\nfunction isBarrelImport(spec) {\n  return /(^|\\/)index(\\.js)?$/.test(spec) && !spec.includes('entry');\n}\nif (dynamicImports.some(isBarrelImport)) console.warn('dynamic import targets a barrel — may hit wrapper bugs');","typeGuard":"fn has_wrapper(state: &OrderWrapState, idx: ModuleIdx, meta: &LinkingMetadata) -> bool {\n  state.esm_init_target(idx, meta).map(|t| !t.wrapper_ref.is_synthetic()).unwrap_or(false)\n}","tryCatchPattern":"try {\n  await bundle.generate();\n} catch (err) {\n  if (String(err).includes('dynamic-entry') && String(err).includes('wrapper')) {\n    // fallback: disable experimental order-wrap options and retry\n    return rebuild({ experimental: {} });\n  }\n  throw err;\n}","preventionTips":["Dynamically import concrete modules, never re-export barrels","Keep experimental order-wrap/consumer-local flags off in production builds until stable","Add dynamic-entry bundling to CI to catch wrapper regressions after upgrades","Run builds in isolated worker threads to contain panics"],"tags":["rust","panic","dynamic-import","code-splitting","internal-bug"],"backgroundTag":"internal-invariant-violation","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"}