astral-sh/ruff · error
expected class
Error message
expected class
What it means
Scope::expect_class unwraps as_class(): it returns the StmtClassDef node only when the scope was introduced by a class statement; any other scope kind (module, function, comprehension, type alias) panics with 'expected class'. It is a convenience for callers that already established the scope kind.
Source
Thrown at crates/ty_python_core/src/scope.rs:424
| Self::ClassTypeParameters(_)
| Self::TypeAliasTypeParameters(_) => ScopeKind::TypeParams,
Self::TypeAlias(_) => ScopeKind::TypeAlias,
Self::ListComprehension(_)
| Self::SetComprehension(_)
| Self::DictComprehension(_)
| Self::GeneratorExpression(_) => ScopeKind::Comprehension,
}
}
pub fn as_class(&self) -> Option<&AstNodeRef<ast::StmtClassDef>> {
match self {
Self::Class(class) => Some(class),
_ => None,
}
}
pub fn expect_class(&self) -> &AstNodeRef<ast::StmtClassDef> {
self.as_class().expect("expected class")
}
pub fn as_function(&self) -> Option<&AstNodeRef<ast::StmtFunctionDef>> {
match self {
Self::Function(function) => Some(function),
_ => None,
}
}
pub fn expect_function(&self) -> &AstNodeRef<ast::StmtFunctionDef> {
self.as_function().expect("expected function")
}
fn as_type_alias(&self) -> Option<&AstNodeRef<ast::StmtTypeAlias>> {
match self {
Self::TypeAlias(type_alias) => Some(type_alias),
_ => None,
}View on GitHub (pinned to d1087a4b9e)
Solutions
- Use as_class() and handle None instead of expect_class when the kind is not statically guaranteed
- Re-verify how the scope id was obtained; class scopes only wrap class-level definitions
- If reached inside ty itself on valid Python, minimize the repro and file an issue
Example fix
// before
let class_node = scope.node().expect_class(); // panics if not a class scope
// after
let Some(class_node) = scope.node().as_class() else { return None; }; Defensive patterns
Strategy: type-guard
Validate before calling
if scope.kind() != ScopeKind::Class { return None; } // establish kind before unwrapping Type guard
let is_class_scope = |scope: &Scope| matches!(scope.kind(), ScopeKind::Class);
Try / catch
match scope.node().as_class() { Some(class) => { /* ... */ }, None => { /* wrong scope kind: skip or report */ } } Prevention
- Prefer as_class() over expect_class unless the kind is proven
- Match on ScopeKind before scope-node access
- For runtime panics inside ty, capture the Python repro and file an issue
When it happens
Trigger: Calling expect_class on a scope that is not a Class scope - either direct API misuse, or a caller whose kind check disagreed with the scope node actually looked up (e.g. assuming a definition's scope is a class scope when it resolved elsewhere).
Common situations: Contributors adding scope-based lookups (pytest class handling does exactly this call) and assuming the scope id always refers to a class; refactors that change which scope a definition resolves to.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- expected function
- expected type alias
- i is bounded by INLINE_MAX_SEGMENTS
- index is bounded by INLINE_MAX_SEGMENTS
- PySourceType always parses into a module
AI-assisted analysis of astral-sh/ruff@d1087a4b9e (2026-08-20).
Data as JSON: /api/errors/b03f95b94578533a.
Report an issue: GitHub.