astral-sh/ruff · error

Expected a Type::Dynamic variant

Error message

Expected a Type::Dynamic variant

What it means

Type::expect_dynamic is the unwrapping counterpart of as_dynamic: it asserts a Type value is the Dynamic variant (Any, Unknown, and friends). It fires when a code path that has already reasoned a type must be dynamic actually holds a different variant, i.e., the dynamic-approximation assumption upstream does not hold.

Source

Thrown at crates/ty_python_semantic/src/types.rs:2419

            Type::Intersection(intersection) => intersection
                .positive(db)
                .iter()
                .any(|ty| ty.is_awaitable(db)),
            _ => false,
        }
    }

    /// Is a value of this type only usable in typing contexts?
    pub fn is_type_check_only(&self, db: &'db dyn Db) -> bool {
        match self {
            Type::ClassLiteral(class_literal) => class_literal.type_check_only(db),
            Type::FunctionLiteral(f) => {
                f.has_known_decorator(db, FunctionDecorators::TYPE_CHECK_ONLY)
            }
            _ => false,
        }
    }

    /// Returns whether this type is marked as deprecated via `@warnings.deprecated`.
    pub fn is_deprecated(&self, db: &'db dyn Db) -> bool {
        match self {
            Type::FunctionLiteral(f) => f.implementation_deprecated(db).is_some(),
            Type::Callable(callable) => callable.deprecated(db).is_some(),
            Type::ClassLiteral(c) => c.deprecated(db).is_some(),
            _ => false,
        }
    }

    /// If the type is a specialized instance of the given `KnownClass`, returns the specialization.
    fn known_specialization(
        &self,
        db: &'db dyn Db,
        env: &ProgramEnvironment<'db>,
        known_class: KnownClass,
    ) -> Option<Specialization<'db>> {
        let class_literal = known_class.try_to_class_literal(db, env)?;

View on GitHub (pinned to 15f3fe6b15)

Solutions

  1. Capture the panic backtrace plus the expression being checked and file a ty issue with a minimal repro
  2. As a contributor: prefer `let Type::Dynamic(d) = ty else { ... }` or as_dynamic() with an explicit non-dynamic fallback at the call site
  3. Check whether a newly added Type variant needs handling in the dynamic-approximation helpers

Example fix

// before
let dynamic = ty.expect_dynamic();

// after
if let Some(dynamic) = ty.as_dynamic() {
    // dynamic path
} else {
    // explicit non-dynamic path
}
Defensive patterns

Strategy: type-guard

Type guard

fn is_dynamic<'db>(ty: Type<'db>) -> bool {
    matches!(ty, Type::Dynamic(_))
}

Prevention

When it happens

Trigger: Calling expect_dynamic on a Type that is not Dynamic - for example after a to_dynamic-style approximation or a match fallback that was expected to produce Any/Unknown but returned another variant, or after a new Type variant is added without updating dynamic-fallback logic.

Common situations: Contributor refactors that replace `if let Type::Dynamic` checks with expect_dynamic; addition of a new Type variant that should participate in dynamic approximations but does not.

Related errors


AI-assisted analysis of astral-sh/ruff@15f3fe6b15 (2026-08-20). Data as JSON: /api/errors/58c6b9ffe73d45b0. Report an issue: GitHub.