rolldown/rolldown · error

consumer-local route should have complete namespace targets

Error message

consumer-local route should have complete namespace targets

What it means

This is an internal invariant panic in rolldown's chunk-generation phase. When a namespace re-export is retained on a consumer-local re-export route, the bundler expects its order-state bookkeeping to already hold the complete list of namespace init targets for that route; `consumer_local_namespace_targets` returning None means that bookkeeping was never populated, i.e. the bundler's wrapped-ESM init ordering logic violated its own contract.

Solutions

  1. Reduce the input to a minimal reproduction (barrel file with circular `export *` + dynamic import) and file an issue at https://github.com/rolldown/rolldown/issues with the repro and rolldown version
  2. Try the latest rolldown version — this invariant is frequently fixed in patch releases
  3. Restructure the offending barrel files to use explicit named re-exports instead of `export *` to sidestep the consumer-local re-export route
  4. As a workaround, disable or adjust advanced chunking options (e.g. `experimental.strictExecutionOrder`, `advancedChunks`) that change wrapped-ESM init ordering
  5. Check whether a specific plugin is generating the re-export graph and bypass/replace it

Example fix

// before (problematic module graph)
// a.js: export * from './b.js'; export const a = 1;
// b.js: export * from './a.js';
// after
// a.js: export { b } from './b.js'; export const a = 1;
// b.js: export const b = 2;
Defensive patterns

Strategy: validation

Validate before calling

// Pre-build sanity check for circular namespace re-exports
import { globSync } from 'node:fs';
// grep source for 'export *' chains; if a cycle exists, restructure before building
function hasExportStarCycle(files) {
  const seen = new Set();
  const visit = (f) => {
    if (seen.has(f)) return true; seen.add(f);
    return (readExportStars(f) || []).some(visit);
  };
  return files.some(visit);
}
if (hasExportStarCycle(entryFiles)) console.warn('circular export * detected: may trip rolldown wrapped-esm init invariant');

Type guard

function isRolldownPanic(err) {
  return err instanceof Error && /consumer-local route should have complete namespace targets/.test(err.message);
}

Try / catch

try {
  await bundle();
} catch (err) {
  if (/consumer-local route should have complete namespace targets/.test(String(err))) {
    console.error('Rolldown internal invariant hit; report repro + version, or flatten export * chains');
    process.exitCode = 1;
  } else throw err;
}

Prevention

When it happens

Trigger: Hitting this requires the chunk graph to contain a consumer-local namespace re-export (`export * from` / namespace re-export of a module that stays within the same chunk group) that is retained in the output while the wrapped-ESM init metadata pass finds no namespace targets recorded for that route. It is reached through normal `Bundle`/`rollup` builds, not a user-facing API call, and almost always indicates a bundler bug triggered by an unusual module graph shape (circular namespace re-exports mixed with dynamic imports or code-splitting).

Common situations: Large codebases with circular `export * from` chains combined with dynamic imports/entry-level code splitting; upgrading rolldown versions where the consumer-local re-export route detection changed; rare graph shapes in monorepos with barrel files that re-export each other.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at crates/rolldown/src/stages/generate_stage/compute_wrapped_esm_init_metadata.rs:284

            namespace: namespace_reexport_is_retained,
          },
        ) {
          continue;
        }
        if stmt_is_included
          && overlay.is_none_or(|overlay| overlay.retained_reexport_path.is_empty())
        {
          continue;
        }
      } else if !is_reexport {
        continue;
      }
      let mut targets = vec![];
      if namespace_reexport_is_retained && ctx.order_state.is_consumer_local_reexport_route(root) {
        let namespace_targets = ctx
          .order_state
          .consumer_local_namespace_targets(root)
          .expect("consumer-local route should have complete namespace targets");
        targets.extend(namespace_targets.iter().copied().filter(|target| match target {
          WrappedEsmInitTarget::Module(module_idx) => {
            ctx.order_state.esm_init_included_in_live_chunk(
              &ctx.metas[*module_idx],
              *module_idx,
              ctx.chunk_graph,
            )
          }
          WrappedEsmInitTarget::CjsCarrier(key) => {
            ctx.order_state.order_cjs_carrier_included_in_live_chunk(*key, ctx.chunk_graph)
          }
        }));
      } else if ctx.order_wrap {
        // A recorded retained path restricts the hop walk to the chains resolved reads consumed.
        // That is only sound when the path is the record's whole evidence: an included namespace
        // (or forwarded dynamic exports) retains EVERY non-ambiguous export of this star record —
        // including chains no resolved read recorded — so the walk must stay unrestricted there or
        // the off-path pure definers lose their only init call site.

View on GitHub (pinned to 91b44b9d7b)