rust-lang/rust · critical
this never happens at the root, we're never in erased mode h
Error message
this never happens at the root, we're never in erased mode here
What it means
Internal compiler panic in the next trait solver's root-goal evaluator. After EvalCtxt::enter_root runs a goal at the recursion limit, the inner result is pattern-matched; the RerunNonErased variant is declared unreachable because root evaluation never executes in 'erased mode' (that mode only exists for nested/canonicalized sub-goals in the search graph). Hitting it means a solver refactor broke the invariant that the root entry point is always non-erased.
Source
Thrown at compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs:258
// No need to try the fast path if stalled_on is `None`, since we already try the fast path
// immediately when adding new goals. If we didn't check `stalled_on` here we'd be trying
// the fast path twice for some goals.
if stalled_on.is_some()
&& let Some(res) = compute_goal_fast_path_cold(self, goal, span)
{
return Ok(res);
}
let mut result = EvalCtxt::enter_root(self, self.cx().recursion_limit(), span, |ecx| {
ecx.evaluate_goal_no_fast_paths(GoalSource::Misc, goal)
});
maybe_evaluate_root_goal_with_higher_recursion_limit(self, goal, span, &mut result);
match result {
Ok(i) => Ok(i),
Err(NoSolutionOrRerunNonErased::NoSolution(NoSolution)) => Err(NoSolution),
Err(NoSolutionOrRerunNonErased::RerunNonErased(_)) => {
unreachable!("this never happens at the root, we're never in erased mode here");
}
}
}
#[instrument(level = "debug", skip(self), ret)]
fn root_goal_may_hold_opaque_types_jank(
&self,
goal: Goal<Self::Interner, <Self::Interner as Interner>::Predicate>,
) -> bool {
self.probe(|| {
EvalCtxt::enter_root(self, self.cx().recursion_limit(), I::Span::dummy(), |ecx| {
ecx.evaluate_goal(GoalSource::Misc, goal, None)
})
.is_ok_and(|r| match r.certainty {
Certainty::Yes => true,
Certainty::Maybe(MaybeInfo {
cause: _,
opaque_types_jank,View on GitHub (pinned to 22057b88b0)
Solutions
- File a rustc issue with the -Znext-solver ICE and the full backtrace; this is a compiler bug, not user error.
- If running a locally built compiler, bisect recent changes to evaluate_root_goal / enter_root / NoSolutionOrRerunNonErased handling.
- Work around by disabling -Znext-solver (or -Znext-solver=coherence for coherence-only) until the invariant is restored.
- Ensure nested goals always handle RerunNonErased before returning to the root, so the variant cannot propagate into enter_root.
Defensive patterns
Strategy: validation
Validate before calling
// Before invoking rustc/cargo, assert the next-solver is NOT enabled for crates that
// rely on erased/abstract root goals. Run as a build-check script.
fn solver_mode_safe() -> bool {
let flags = std::env::var("RUSTFLAGS").unwrap_or_default();
let cargo_flags = std::env::var("CARGO_ENCODED_RUSTFLAGS").unwrap_or_default();
!(flags.contains("next-solver") || cargo_flags.contains("next-solver"))
}
#[test]
fn assert_no_next_solver_at_root() { assert!(solver_mode_safe()); } Try / catch
// Compiling programmatically? Isolate the compiler call so an ICE cannot abort the host.
use std::panic::{catch_unwind, AssertUnwindSafe};
let outcome = catch_unwind(AssertUnwindSafe(|| {
// crate::my_proc_macro::expand_root(...) or rustc invocation
}));
if outcome.is_err() { /* report ICE, keep host alive */ } Prevention
- Pin to a stable rustc toolchain; `-Znext-solver` is nightly-only and reflows invariants frequently.
- Do not enable the next solver project-wide; gate it behind a per-crate profile so root-level goals keep using the legacy solver.
- Keep a `rust-toolchain.toml` committed so CI never picks a bleeding-edge compiler that re-introduces the bug.
- Run `cargo build` with `RUST_BACKTRACE=1` only for diagnosis, never as a normal flow — it masks nothing and adds noise.
When it happens
Trigger: Reached if EvalCtxt::enter_root returns Err(NoSolutionOrRerunNonErased::RerunNonErased(_)) from evaluate_root_goal in compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs:257. This requires an inner evaluate_goal_no_fast_paths call to signal a non-erased rerun that propagates all the way up through enter_root instead of being handled at a nested level.
Common situations: Almost always a regression introduced while refactoring the solver's erased/non-erased evaluation paths (e.g. trait-system-refactor-initiative changes). End users only hit it as an ICE on arbitrary crates; it is not caused by user source code.
Related errors
- unexpected orig_value: {ty:?}
- unexpected orig_value: {ct:?}
- ConstEquate should not be emitted when `-Znext-solver` is ac
- unexpected nested goal in `relate`: {p:?}
- Params should have been canonicalized to placeholders
AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03).
Data as JSON: /data/errors/da8227ff8808d03d.json.
Report an issue: GitHub.