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
- File a ty issue with the expression triggering negation (typically `not x` narrowing) and the backtrace
- As a contributor: implement the missing negation in negated_divergent, or route that variant through the IntersectionBuilder path instead of the fast path
- 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
- Run the negation property test (all_negated_types_identical_to_intersection_with_single_negated_element) after touching negate() or Divergent
- When adding a Divergent sub-case, either implement its negation or route it through the IntersectionBuilder path
- Test `not x` narrowing for every new divergent construct
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
- The meta-type of an instance-like type should always have an
- Expected a Type::Dynamic variant
- `find_name_in_mro` should return `Some` for a class literal
- extra use-def data should have been retained
- should be set because `extract_if` only yields elements with
AI-assisted analysis of astral-sh/ruff@15f3fe6b15 (2026-08-20).
Data as JSON: /api/errors/c4b7a3a4c86ecf8f.
Report an issue: GitHub.