astral-sh/ruff · info
Empty `else` clause
Error message
Empty `else` clause
What it means
An internal guard in the useless-else-on-loop (PLW0120) fix removal path. When deleting a loop's `else` clause, the fixer takes the first and last statements of the `orelse` body to compute the deletion range. If the `else` body is empty (`orelse.first()` is None), there is nothing to remove and the fix aborts with 'Empty `else` clause'.
Source
Thrown at crates/ruff_linter/src/rules/pylint/rules/useless_else_on_loop.rs:141
Stmt::For(ast::StmtFor { orelse, .. }) | Stmt::While(ast::StmtWhile { orelse, .. }) => {
loop_exits_early(orelse)
}
Stmt::Break(_) => true,
_ => false,
})
}
/// Generate a [`Fix`] to remove the `else` clause from the given statement.
fn remove_else(
stmt: &Stmt,
orelse: &[Stmt],
else_range: TextRange,
locator: &Locator,
indexer: &Indexer,
stylist: &Stylist,
) -> Result<Fix> {
let Some(start) = orelse.first() else {
return Err(anyhow::anyhow!("Empty `else` clause"));
};
let Some(end) = orelse.last() else {
return Err(anyhow::anyhow!("Empty `else` clause"));
};
let start_indentation = indentation(locator.contents(), start);
if start_indentation.is_none() {
// Inline `else` block (e.g., `else: x = 1`).
Ok(Fix::safe_edit(Edit::deletion(
else_range.start(),
start.start(),
)))
} else {
// Identify the indentation of the loop itself (e.g., the `while` or `for`).
let Some(desired_indentation) = indentation(locator.contents(), stmt) else {
return Err(anyhow::anyhow!("Compound statement cannot be inlined"));
};
View on GitHub (pinned to 26f38c119c)
Solutions
- Treat the Err as 'nothing to fix' and skip the fix
- Delete the empty `else:` line manually if it is genuinely useless
- Ensure the rule's caller only requests a fix when `orelse` contains at least one statement
Defensive patterns
Strategy: type-guard
Validate before calling
def has_else_body(orelse: list) -> bool:
return len(orelse) > 0 Type guard
def has_statements(orelse: list[ast.stmt] | None) -> bool:
return bool(orelse) Prevention
- Only request the PLW0120 fix when the loop `else` has at least one statement
- Filter comment-only else bodies before fix generation
When it happens
Trigger: `remove_else` (called from `useless_else_on_loop`) is invoked with an `orelse` slice whose first statement is None — an `else:` with no body statements reaching the fix stage (normally the diagnostic only fires for non-empty else bodies, so this is a defensive path).
Common situations: Rarely hit by end users; mostly surfaced when the rule's fix logic is called with an `else` clause containing only comments/no statements, or by tooling invoking the fix API directly.
Related errors
- Compound statement cannot be inlined
- `else` is expected to be on its own line
- indented block to start with indentation
- {value:?} cannot be converted to EmptyStringCmpOp
- Failed to collapse `with`: {err}
AI-assisted analysis of astral-sh/ruff@26f38c119c (2026-09-05).
Data as JSON: /api/errors/00ef3e1316f651a8.
Report an issue: GitHub.