astral-sh/ruff · error
expected function
Error message
expected function
What it means
Scope::expect_function unwraps as_function(): it returns the StmtFunctionDef node only when the scope was introduced by a function (or lambda) statement; any other scope kind panics with 'expected function'. Callers like function.rs use it on body scopes where the kind is known by construction.
Source
Thrown at crates/ty_python_core/src/scope.rs:435
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,
}
}
pub fn expect_type_alias(&self) -> &AstNodeRef<ast::StmtTypeAlias> {
self.as_type_alias().expect("expected type alias")
}
/// Returns the anchor node index for this scope, or `None` for the module scope.
///
/// This is used to compute relative node indices for expressions within the scope,
/// providing a stable anchor that only changes when the scope-introducing node changes.
pub fn node_index(&self) -> Option<NodeIndex> {View on GitHub (pinned to d1087a4b9e)
Solutions
- Use as_function() and handle None when the scope kind is not guaranteed
- Match on the scope kind (ScopeKind::Function) before calling expect_*
- If it fires inside ty on valid Python, minimize the repro and file an issue
Example fix
// before
let fn_node = body_scope.node(db).expect_function(); // panics if not a function scope
// after
let Some(fn_node) = body_scope.node(db).as_function() else { return None; }; Defensive patterns
Strategy: type-guard
Validate before calling
if scope.kind() != ScopeKind::Function { return None; } // establish kind before unwrapping Type guard
let is_function_scope = |scope: &Scope| matches!(scope.kind(), ScopeKind::Function);
Try / catch
match scope.node().as_function() { Some(func) => { /* ... */ }, None => { /* wrong scope kind: skip or report */ } } Prevention
- Use as_function() when the scope provenance is not guaranteed
- Derive scope ids from known constructs (e.g. function body scopes) rather than guesses
- Keep expect_* calls co-located with the kind check that justifies them
When it happens
Trigger: Calling expect_function on a module, class, comprehension, or type-alias scope - direct misuse, or a lookup path that fetched a different scope than assumed (e.g. a definition whose enclosing scope is not the function body).
Common situations: Contributors writing new scope-based queries without matching on ScopeKind first; semantic-model refactors shifting which scope a node belongs 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 class
- 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/d4038e9be15f6994.
Report an issue: GitHub.