astral-sh/ruff · error
else clause
Error message
else clause
What it means
An .expect in the PLW0120 useless-else-on-loop rule: the code tries to locate the `else` keyword of a loop statement that the earlier guard confirmed has a non-empty orelse block. Firing it means the AST/range lookup for the else identifier failed despite the block existing — an internal invariant break, not a user error.
Source
Thrown at crates/ruff_linter/src/rules/pylint/rules/useless_else_on_loop.rs:75
#[derive_message_formats]
fn message(&self) -> String {
"`else` clause on loop without a `break` statement; \
remove the `else` and dedent its contents"
.to_string()
}
fn fix_title(&self) -> Option<String> {
Some("Remove `else`".to_string())
}
}
/// PLW0120
pub(crate) fn useless_else_on_loop(checker: &Checker, stmt: &Stmt, body: &[Stmt], orelse: &[Stmt]) {
if orelse.is_empty() || loop_exits_early(body) {
return;
}
let else_range = identifier::else_(stmt, checker.locator().contents()).expect("else clause");
let mut diagnostic = checker.report_diagnostic(UselessElseOnLoop, else_range);
diagnostic.try_set_fix(|| {
remove_else(
stmt,
orelse,
else_range,
checker.locator(),
checker.indexer(),
checker.stylist(),
)
});
}
/// Returns `true` if the given body contains a `break` statement.
fn loop_exits_early(body: &[Stmt]) -> bool {
body.iter().any(|stmt| match stmt {
Stmt::If(ast::StmtIf {View on GitHub (pinned to 26f38c119c)
Solutions
- Use the orelse block's range directly instead of re-locating the else keyword
- Skip the diagnostic when the else identifier cannot be located
Defensive patterns
Strategy: type-guard
When it happens
Trigger: Thrown at crates/ruff_linter/src/rules/pylint/rules/useless_else_on_loop.rs:75 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of astral-sh/ruff@26f38c119c (2026-09-05).
Data as JSON: /api/errors/e8b141dd3ef2f8be.
Report an issue: GitHub.