astral-sh/ruff · warning
`else` is expected to be on its own line
Error message
`else` is expected to be on its own line
What it means
This is a fix-generation failure in pylint's collapsible-else-if (PLR5501) fix, which rewrites an `else: if ...` block into an `elif`. The fixer needs the indentation of the `else` clause to dedent the inner `if` correctly; Ruff's `indentation()` returns None for clauses not starting on their own line, so the fix aborts with this message.
Source
Thrown at crates/ruff_linter/src/rules/pylint/rules/collapsible_else_if.rs:119
/// Generate [`Fix`] to convert an `else` block to an `elif` block.
fn convert_to_elif(
first: &Stmt,
else_clause: &ElifElseClause,
locator: &Locator,
indexer: &Indexer,
stylist: &Stylist,
) -> Result<Fix> {
let inner_if_line_start = locator.line_start(first.start());
let inner_if_line_end = locator.line_end(first.end());
// Capture the trivia between the `else` and the `if`.
let else_line_end = locator.full_line_end(else_clause.start());
let trivia_range = TextRange::new(else_line_end, inner_if_line_start);
// Identify the indentation of the outer clause
let Some(indentation) = indentation(locator.contents(), else_clause) else {
return Err(anyhow::anyhow!("`else` is expected to be on its own line"));
};
// Dedent the content from the end of the `else` to the end of the `if`.
let indented = adjust_indentation(
TextRange::new(inner_if_line_start, inner_if_line_end),
indentation,
locator,
indexer,
stylist,
)?;
// If there's trivia, restore it
let trivia = if trivia_range.is_empty() {
None
} else {
let indented_trivia =
adjust_indentation(trivia_range, indentation, locator, indexer, stylist)?;
Some(Edit::insertion(View on GitHub (pinned to 26f38c119c)
Solutions
- Put `else:` on its own line with standard indentation, keeping the nested `if` on the next line, then rerun the fix
- Manually rewrite the nested `else: if` into an `elif`
- Normalize tabs to spaces (or configure Ruff's indentation settings) so indentation detection succeeds
Example fix
# before (fix fails)
if a:
f()
else: if b:
g()
# after
if a:
f()
elif b:
g() Defensive patterns
Strategy: validation
Validate before calling
import re
def elif_fix_candidate(source: str) -> bool:
# else must be alone on its line, consistently indented
return re.search(r"^\s*else:\s*$", source, re.M) is not None Prevention
- Never write `else: if ...` on one line
- Use a single indentation style (spaces) throughout the file
- Run `ruff format` before `ruff --fix`
When it happens
Trigger: `convert_to_elif` (called from `collapsible_else_if`) encounters a nested `if` inside an `else:` where the `else` keyword is not on its own line (e.g. `else: if cond:` on one line) or its indentation cannot be detected (mixed tabs, unusual layout).
Common situations: Autofixing compact one-line `else: if ...` code, generated or minified Python, or files mixing tabs and spaces; users of `ruff --fix` see PLR5501 reported but not fixed.
Related errors
- indented block to start with indentation
- Compound statement cannot be inlined
- Compound statement cannot be inlined
- Empty `else` clause
- {value:?} cannot be converted to EmptyStringCmpOp
AI-assisted analysis of astral-sh/ruff@26f38c119c (2026-09-05).
Data as JSON: /api/errors/63e0ef8896227a17.
Report an issue: GitHub.