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

  1. Put `else:` on its own line with standard indentation, keeping the nested `if` on the next line, then rerun the fix
  2. Manually rewrite the nested `else: if` into an `elif`
  3. 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

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


AI-assisted analysis of astral-sh/ruff@26f38c119c (2026-09-05). Data as JSON: /api/errors/63e0ef8896227a17. Report an issue: GitHub.