rust-lang/rust · critical

Error: trying to record dependency on DepNode {dep_node} in

Error message

Error: trying to record dependency on DepNode {dep_node} in a context that does not allow it (e.g. during query deserialization). The most common case of recording a dependency on a DepNode `foo` is when the corresponding query `foo` is invoked. Invoking queries is not allowed as part of loading something from the incremental on-disk cache. See <https://github.com/rust-lang/rust/pull/91919>.

What it means

Internal compiler error (ICE) from panic_on_forbidden_read (compiler/rustc_middle/src/dep_graph/graph.rs:1446), reached via DepGraph::read_index when the current task's dependency-tracking ref is TaskDepsRef::Forbid. That state is used while deserializing/loading values from the incremental on-disk cache, where recording a new query dependency would create a cycle or a non-reproducible graph. The message names the offending DepNode and points at PR #91919 which introduced the forbid context.

Source

Thrown at compiler/rustc_middle/src/dep_graph/graph.rs:1446

    // corresponds to `dep_node_index`, but that's OK since we are about
    // to ICE anyway.
    let mut dep_node = None;

    // First try to find the dep node among those that already existed in the
    // previous session and has been marked green
    for prev_index in data.colors.values.indices() {
        if data.colors.current(prev_index) == Some(dep_node_index) {
            dep_node = Some(*data.previous.index_to_node(prev_index));
            break;
        }
    }

    let dep_node = dep_node.map_or_else(
        || format!("with index {:?}", dep_node_index),
        |dep_node| format!("`{:?}`", dep_node),
    );

    panic!(
        "Error: trying to record dependency on DepNode {dep_node} in a \
         context that does not allow it (e.g. during query deserialization). \
         The most common case of recording a dependency on a DepNode `foo` is \
         when the corresponding query `foo` is invoked. Invoking queries is not \
         allowed as part of loading something from the incremental on-disk cache. \
         See <https://github.com/rust-lang/rust/pull/91919>."
    )
}

impl<'tcx> TyCtxt<'tcx> {
    /// Return whether this kind always require evaluation.
    #[inline(always)]
    fn is_eval_always(self, kind: DepKind) -> bool {
        self.dep_kind_vtable(kind).is_eval_always
    }
}

View on GitHub (pinned to 22057b88b0)

Solutions

  1. Report the ICE to the rustc team with the DepNode name and a reproducer; this is a compiler-internal invariant violation, not user code.
  2. If you are writing a custom query provider, ensure it does not call other tcx queries during decode/deserialize paths — read from the cached DepNode instead.
  3. Disable incremental compilation (-C incremental=no) or wipe the incremental cache directory to rule out cache corruption causing a different code path.
  4. Bisect across nightly rustc versions to find the commit that introduced the query call inside the forbidden context (PR #91919 is the reference point).
Defensive patterns

Strategy: fallback

Validate before calling

// No pre-call validation is possible: the error fires deep inside
// query deserialization from the incremental cache. Mitigate by
// disabling incremental compilation for the affected crate.
fn build_cmd_no_incremental() -> std::process::Command {
    let mut cmd = std::process::Command::new("cargo");
    cmd.args(["build", "--release", "-C", "incremental=no"]);
    cmd.env_remove("CARGO_INCREMENTAL");
    cmd
}

Try / catch

// For tool authors driving rustc as a library via rustc_interface::run_compiler,
// isolate the compiler callback in catch_unwind so an ICE does not tear down
// the host process.
use std::panic::{catch_unwind, AssertUnwindSafe};

let result = catch_unwind(AssertUnwindSafe(|| {
    rustc_interface::run_compiler(config, |tcx| {
        // ... perform queries that may trigger incremental cache loading ...
    })
}));
match result {
    Ok(value) => /* use value */ { let _ = value; }
    Err(payload) => {
        eprintln!("rustc ICE during incremental cache load; falling back to clean build");
        // Delete target/<profile>/incremental and retry once without -Zincremental
    }
}

Prevention

When it happens

Trigger: A query is invoked (transitively) from inside the on-disk cache loading path, e.g. decoding a SerializedDepGraph entry, decoding a query result, or running an eval_always query while TaskDepsRef is Forbid. Concretely, read_index(dep_node_index) is called while the thread-local TaskDepsRef equals Forbid.

Common situations: Compiler bugs where a deserializer/decoder calls into tcx.<query>(...) instead of reading the cached value; custom rustc query providers that invoke other queries during deserialization; regressions after refactors that move query calls into the decode path.

Related errors


AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03). Data as JSON: /data/errors/425140e2b643a440.json. Report an issue: GitHub.