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

  1. Split the oversized crate into smaller sub-crates so no single crate exceeds the node budget.
  2. Reduce generated/macro-expanded content; avoid unrolling huge data into source.
  3. Move large data into include_bytes!/include_str! or a build step rather than literal AST nodes.
  4. 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

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


AI-assisted analysis of rust-lang/rust@7088e4b63a (2026-08-10). Data as JSON: /api/errors/fb62a93132bafb56. Report an issue: GitHub.