astral-sh/ruff · info

Expected indented block to have at least one statement

Error message

Expected indented block to have at least one statement

What it means

During the SIM102 collapse, the outer body is temporarily embedded in a function definition so libcst exposes an `IndentedBlock`; if that block has no statements (`body.first_mut()` is `None`) the fix bails, since a merged `if` needs at least one statement to move under it.

Source

Thrown at crates/ruff_linter/src/rules/flake8_simplify/rules/collapsible_if.rs:355

        Cow::Owned(format!(
            "def f():{}{module_text}",
            stylist.line_ending().as_str()
        ))
    };

    // Parse the CST.
    let mut tree = match_statement(&module_text)?;

    let statement = if outer_indent.is_empty() {
        &mut tree
    } else {
        let embedding = match_function_def(&mut tree)?;

        let indented_block = match_indented_block(&mut embedding.body)?;
        indented_block.indent = Some(outer_indent);

        let Some(statement) = indented_block.body.first_mut() else {
            bail!("Expected indented block to have at least one statement")
        };
        statement
    };

    let outer_if = match_if(statement)?;

    let libcst_native::If {
        body: libcst_native::Suite::IndentedBlock(outer_body),
        orelse: None,
        ..
    } = outer_if
    else {
        bail!("Expected outer if to have indented body and no else")
    };

    let [
        libcst_native::Statement::Compound(libcst_native::CompoundStatement::If(
            inner_if @ libcst_native::If { orelse: None, .. },

View on GitHub (pinned to 15f3fe6b15)

Solutions

  1. Ensure the innermost `if` body contains at least one statement (e.g. add `pass` or the real body) and rerun the fix
  2. Collapse the conditions manually into a single `if a and b:`
  3. Suppress SIM102 with `# noqa` until the body is filled in

Example fix

// before
if a:
    if b:
        pass  # only statement
// after
if a and b:
    pass
Defensive patterns

Strategy: validation

Validate before calling

python - <<'EOF'
import ast, sys
tree = ast.parse(open(sys.argv[1]).read())
for n in ast.walk(tree):
    if isinstance(n, ast.If) and len(n.body) == 1 and isinstance(n.body[0], ast.If):
        inner = n.body[0]
        if len(inner.body) == 0:
            print('empty nested-if body at line', inner.lineno, '- not SIM102-fixable')
EOF

Prevention

When it happens

Trigger: A nested `if a: if b:` where the innermost body is empty or unparseable into a first statement — practically `if` blocks containing only comments or pass-less bodies produced by partial edits.

Common situations: Work-in-progress code with empty blocks, code stripped by other tools leaving only comments, or stub files.

Related errors


AI-assisted analysis of astral-sh/ruff@15f3fe6b15 (2026-09-05). Data as JSON: /api/errors/21a791c772adde2f. Report an issue: GitHub.