rust-lang/rust · critical
unexpected orig_value: {ty:?}
Error message
unexpected orig_value: {ty:?} What it means
Invariant check in EvalCtxt::build_stalled_on while classifying the 'orig values' of a stalled canonical goal. After stripping universal (placeholder) vars, each remaining generic arg's type is expected to be an inference variable (TyVar/IntVar/FloatVar) or, as a no-op case, a Param/Placeholder. Any other TyKind (e.g. a rigid Alias, a concrete Adt, a Bound) means canonicalization or unification left a non-infer type in the existential orig_values, which the stalled-goal machinery cannot represent.
Source
Thrown at compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs:866
&self,
canonical_goal: CanonicalInput<I>,
certainty: Certainty,
mut stalled_vars: ThinVec<I::GenericArg>,
previously_succeeded_in_erased: SucceededInErased<I>,
) -> GoalStalledOn<I> {
// Remove the canonicalized universal vars, since we only care about stalled existentials.
let mut sub_roots = ThinVec::new();
stalled_vars.retain(|arg| match arg.kind() {
// Lifetimes can never stall goals.
ty::GenericArgKind::Lifetime(_) => false,
ty::GenericArgKind::Type(ty) => match ty.kind() {
ty::Infer(ty::TyVar(vid)) => {
sub_roots.push(self.delegate.sub_unification_table_root_var(vid));
true
}
ty::Infer(_) => true,
ty::Param(_) | ty::Placeholder(_) => false,
_ => unreachable!("unexpected orig_value: {ty:?}"),
},
ty::GenericArgKind::Const(ct) => match ct.kind() {
ty::ConstKind::Infer(_) => true,
ty::ConstKind::Param(_) | ty::ConstKind::Placeholder(_) => false,
_ => unreachable!("unexpected orig_value: {ct:?}"),
},
});
GoalStalledOn {
stalled_vars,
sub_roots,
stalled_certainty: certainty,
opaques: GoalStalledOnOpaques::Yes {
num_opaques_in_storage: canonical_goal
.canonical
.value
.predefined_opaques_in_body
.len(),View on GitHub (pinned to 22057b88b0)
Solutions
- Report the ICE to rustc with the reproduction and the {ty:?} value printed in the panic.
- Avoid -Znext-solver if the ICE blocks your build until the canonicalization bug is fixed.
- If hacking on the solver, audit the upstream code that populates orig_values / stalled_vars to confirm only Infer/Param/Placeholder kinds are inserted.
- Bisect solver changes around opaque types and canonicalization, since rigid aliases are the most likely illegal kind to leak in.
Defensive patterns
Strategy: type-guard
Validate before calling
// Walk the crate's type surface (e.g. via `cargo-expand` AST) and reject types whose
// `TyKind` does not round-trip through `orig_value`. Simplified check:
fn ty_is_solver_safe(ty: &syn::Type) -> bool {
match ty {
syn::Type::Path(_) | syn::Type::Reference(_) | syn::Type::Array(_) | syn::Type::Slice(_) => true,
// opaque/impl-trait and bare inference vars at definition sites are the usual trigger
syn::Type::ImplTrait(_) | syn::Type::Infer(_) | syn::Type::Macro(_) => false,
_ => true,
}
} Type guard
// Rust: a const-evaluable narrowing guard over `&dyn Any` won't help at compile time,
// so encode the invariant as a where-clause the solver accepts before expansion:
trait SolverSafeType: Sized {}
impl<T: 'static> SolverSafeType for T where T: core::fmt::Debug {}
fn accept<T: SolverSafeType>(v: T) -> T { v } Try / catch
use std::panic::{catch_unwind, AssertUnwindSafe};
let r = catch_unwind(AssertUnwindSafe(|| expand_type(input)));
match r { Ok(ty) => ty, Err(_) => fallback_to_legacy_solver(input) } Prevention
- Avoid surfacing `impl Trait` in positions where the solver must relate it to an already-substituted `orig_value`.
- Prefer concrete generic params over inferred/anonymous types at FFI and macro expansion boundaries.
- Run `cargo check` early and often on type-heavy changes; this panic is reachable purely from `check`.
When it happens
Trigger: Triggered when build_stalled_on (compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs:847) iterates stalled_vars and a GenericArgKind::Type whose kind is neither Infer nor Param/Placeholder is encountered — for example an opaque/alias type or a concrete type leaking into the canonical goal's orig_values.
Common situations: Surfaces as an ICE during trait solving when a goal stalls on inference variables. Typically a compiler bug in canonicalization, opaque-type handling, or a recent change to what kinds may appear in orig_values; not reproducible from normal user code except as the trigger input to the buggy path.
Related errors
- unexpected orig_value: {ct:?}
- Params should have been canonicalized to placeholders
- this never happens at the root, we're never in erased mode h
- ConstEquate should not be emitted when `-Znext-solver` is ac
- unexpected nested goal in `relate`: {p:?}
AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03).
Data as JSON: /data/errors/66214520a0236005.json.
Report an issue: GitHub.