astral-sh/ruff · error

matched `Type::Divergent` above

Error message

matched `Type::Divergent` above

What it means

Type::negate special-cases Type::Divergent through negated_divergent(), which returns an Option because not every divergent type has a meaningful negation. Inside the Divergent match arm the Option is unwrapped: the arm guarantees the outer variant, so a None means a divergent sub-case without negation support reached the fast path that bypasses the IntersectionBuilder.

Source

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

    /// Returns `Some(UnionType)` if this type behaves like a union. Apart from explicit unions,
    /// this returns `Some` for `TypeAlias`es of unions and `NewType`s of `float` and `complex`.
    fn as_union_like(self, db: &'db dyn Db) -> Option<UnionType<'db>> {
        match self.resolve_type_alias(db) {
            Type::Union(union) => Some(union),
            Type::NewTypeInstance(newtype) => newtype.concrete_base_type(db).as_union_like(db),
            _ => None,
        }
    }

    const fn as_dynamic(self) -> Option<DynamicType<'db>> {
        match self {
            Type::Dynamic(dynamic_type) => Some(dynamic_type),
            _ => None,
        }
    }

    const fn as_callable(self) -> Option<CallableType<'db>> {
        match self {
            Type::Callable(callable_type) => Some(callable_type),
            _ => None,
        }
    }

    const fn expect_dynamic(self) -> DynamicType<'db> {
        self.as_dynamic().expect("Expected a Type::Dynamic variant")
    }

    const fn as_protocol_instance(self) -> Option<ProtocolInstanceType<'db>> {
        match self {
            Type::ProtocolInstance(instance) => Some(instance),
            _ => None,
        }
    }

    #[cfg(test)]

View on GitHub (pinned to 15f3fe6b15)

Solutions

  1. File a ty issue with the expression triggering negation (typically `not x` narrowing) and the backtrace
  2. As a contributor: implement the missing negation in negated_divergent, or route that variant through the IntersectionBuilder path instead of the fast path
  3. Re-run the referenced property test after any change to negation logic
Defensive patterns

Strategy: fallback

Type guard

fn has_negation_divergent(ty: Type<'_>) -> bool {
    // mirrors negated_divergent()'s Option contract
    ty.negated_divergent().is_some()
}

Try / catch

let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| check_file(&db, file)));
match result {
    Ok(diags) => diags,
    Err(payload) => { log::warn!("ty panicked during negation of {}: {:?}", file, payload); vec![] }
}

Prevention

When it happens

Trigger: Negating a Divergent type whose kind has no negation rule - a newly added Divergent variant for which negated_divergent returns None - while the fast path in negate() skips the general intersection machinery that would otherwise handle it.

Common situations: Contributor additions of new divergent-type flavors (TypeIs-like constructs) without a negation rule; refactors of the negation fast path. The property test all_negated_types_identical_to_intersection_with_single_negated_element guards the equivalence.

Related errors


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