rust-lang/rust · critical
input too large; ran out of NodeIds
Error message
input too large; ran out of NodeIds
What it means
rustc_ast_lowering assigns a u32-backed NodeId to every AST node (lib.rs:770). next_node_id does checked_add(1).expect("input too large; ran out of NodeIds"); if the counter would exceed u32::MAX the compiler panics. This is a hard limit on the number of nodes in one crate.
Source
Thrown at compiler/rustc_ast_lowering/src/lib.rs:770
def_kind,
self.tcx.hir_def_key(self.local_def_id(node_id)),
);
let def_id = self
.tcx
.at(span)
.create_def(parent, name, def_kind, None, &mut self.current_disambiguator)
.def_id();
debug!("create_def: def_id_to_node_id[{:?}] <-> {:?}", def_id, node_id);
self.node_id_to_def_id.insert(node_id, def_id);
def_id
}
fn next_node_id(&mut self) -> NodeId {
let start = self.next_node_id;
let next = start.as_u32().checked_add(1).expect("input too large; ran out of NodeIds");
self.next_node_id = NodeId::from_u32(next);
start
}
/// Given the id of some node in the AST, finds the `LocalDefId` associated with it by the name
/// resolver (if any).
#[instrument(level = "trace", skip(self), ret)]
fn opt_local_def_id(&self, node: NodeId) -> Option<LocalDefId> {
self.node_id_to_def_id
.get(&node)
.or_else(|| self.owner.node_id_to_def_id.get(&node))
.copied()
}
fn local_def_id(&self, node: NodeId) -> LocalDefId {
self.opt_local_def_id(node).unwrap_or_else(|| {
self.resolver.owners.items().any(|(id, items)| {
items.node_id_to_def_id.items().any(|(node_id, def_id)| {View on GitHub (pinned to 7088e4b63a)
Solutions
- Split the oversized crate into smaller sub-crates so no single crate exceeds the node budget.
- Reduce generated/macro-expanded content; avoid unrolling huge data into source.
- Move large data into include_bytes!/include_str! or a build step rather than literal AST nodes.
- If genuinely needed, track the upstream issue for wider NodeId; for now the only fix is smaller crates.
Example fix
// before: one crate with millions of generated items // (generated.rs: 5_000_000 fn items) // after: shard generated items across submodules compiled as separate crates // crate a: mod m1; crate b: mod m2; ... link them
Defensive patterns
Strategy: validation
Prevention
- Split oversized generated crates into smaller sub-crates so each stays well under the u32 node budget.
- Prefer include_bytes!/include_str! or build-time binary assets over literal unrolled AST nodes.
- Audit build.rs and proc-macro output for runaway expansion before compiling.
- Track the upstream issue for a wider NodeId; until then, smaller crates are the only mitigation.
When it happens
Trigger: Compiling/generated input with more than ~4.29 billion AST nodes in a single crate, almost always heavily generated code (large build.rs output, macro explosions, machine-generated bindings).
Common situations: Code generators (e.g. giant #[derive] expansions, build-script-generated source, vendored data tables) that expand into an enormous single crate; pathological macro recursion.
Related errors
- Invalid input data path: '{path}'\nIf test data has not been
- Cannot bless invalid SBTypeMember object
- Cannot bless invalid SBType object
- Cannot bless invalid child
- Cannot bless invalid SBValue object
AI-assisted analysis of rust-lang/rust@7088e4b63a (2026-08-10).
Data as JSON: /api/errors/fb62a93132bafb56.
Report an issue: GitHub.