astral-sh/ruff · error
Expected Stmt::Import | Stmt::ImportFrom
Error message
Expected Stmt::Import | Stmt::ImportFrom
What it means
Internal panic in Ruff's isort rule annotation pass. `annotate_imports` walks statements that the import-block scanner has already classified as import statements, and only matches `Stmt::Import` and `Stmt::ImportFrom`; any other statement reaching it is considered impossible. The panic means the statement filter upstream produced a non-import statement — a Ruff bug or an internal API misuse.
Source
Thrown at crates/ruff_linter/src/rules/isort/annotate.rs:173
trailing.push(comment);
}
AnnotatedImport::ImportFrom {
module: module.as_ref().map(|module| locator.slice(module)),
names: aliases,
level: *level,
is_lazy: *is_lazy,
trailing_comma: if split_on_trailing_comma {
trailing_comma(import, tokens)
} else {
TrailingComma::default()
},
atop,
inline,
trailing,
}
}
_ => panic!("Expected Stmt::Import | Stmt::ImportFrom"),
}
})
.collect()
}
View on GitHub (pinned to 26f38c119c)
Solutions
- Upgrade Ruff to the latest release and re-run; if it persists, minimize the file with `ruff check --isolated --select I001 <file>` and file an issue
- Run with `--no-fix` or disable isort (`ignore = ["I001"]`) to continue linting while avoiding the crashing path
- If you drive the isort APIs from Rust, ensure the statement list passed to `annotate_imports` contains only `Stmt::Import`/`Stmt::ImportFrom` (filter or match before calling)
- Check for plugins/forks that modify the AST before sorting and remove them
Example fix
// before (caller side, Rust) format_imports(stmts.iter(), ...) // after classifier.filter(stmts.iter().filter(|stmt| matches!(stmt, Stmt::Import(_) | Stmt::ImportFrom(_)))) format_imports(filtered, ...)
Defensive patterns
Strategy: validation
Validate before calling
// Rust: filter to import statements before annotating
let imports: Vec<&Stmt> = stmts
.iter()
.filter(|s| matches!(s, Stmt::Import(_) | Stmt::ImportFrom(_)))
.collect(); Type guard
fn is_import_stmt(stmt: &Stmt) -> bool {
matches!(stmt, Stmt::Import(_) | Stmt::ImportFrom(_))
} Try / catch
// isort runs in the linter process; guard the whole check invocation
let result = std::panic::catch_unwind(|| ruff_check_isort(path))
.map_err(|_| "isort annotation panicked"); Prevention
- Never insert non-import statements into blocks passed to isort annotation APIs
- Avoid AST-mutating plugins/forks that run before import sorting
- Run `ruff check --select I001` on new Ruff versions in CI before rolling out
- Report crashes with `--isolated` reproductions upstream
When it happens
Trigger: Calling `format_imports`/`annotate_imports` with a block of statements containing a non-import statement (e.g. because the traversal handed an `Expr` or `Assign` into the import annotator). Not triggerable by normal `ruff check` input unless there is a classification regression in the isort block scanner.
Common situations: Custom distributions of Ruff where a rule or transformer inserts/permutes statements before isort processing; a Ruff upgrade after which import-block boundaries are computed incorrectly; fuzzed/obfuscated Python that breaks block classification.
Related errors
- Expected Stmt::ImportFrom
- Expected ScopeKind::Function | ScopeKind::Lambda
- Expected ScopeKind::Function
- Expected CmpOp::Is | CmpOp::IsNot
- extra use-def data should have been retained
AI-assisted analysis of astral-sh/ruff@26f38c119c (2026-09-05).
Data as JSON: /api/errors/d1511936aad452f0.
Report an issue: GitHub.