astral-sh/ruff · error

Expected indented block to have at least one statement

Error message

Expected indented block to have at least one statement

What it means

This is an internal fix-generation failure in Ruff's SIM117 (multiple with statements) rule. The rule matched a function definition whose body is a single `with` block, but the indented block parsed as empty (no statements). Since there is no statement to descend into and merge, the autofix bails instead of producing a corrupt edit.

Source

Thrown at crates/ruff_linter/src/rules/flake8_simplify/rules/fix_with.rs:50

    let module_text = if outer_indent.is_empty() {
        contents.to_string()
    } else {
        format!("def f():{}{contents}", 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_with = match_with(statement)?;

    let With {
        body: Suite::IndentedBlock(outer_body),
        ..
    } = outer_with
    else {
        bail!("Expected outer with to have indented body")
    };

    let [Statement::Compound(CompoundStatement::With(inner_with))] = &mut *outer_body.body else {
        bail!("Expected one inner with statement");
    };

View on GitHub (pinned to 26f38c119c)

Solutions

  1. Report the failing snippet to the Ruff project; this bail indicates the autofix matcher hit an unexpected AST shape
  2. Remove or reformat the offending empty/oddly-indented block manually and re-run `ruff --fix`
  3. Run with `--no-fix` or `--fix-only` disabled for that rule (ignore SIM117) to skip the fixer

Example fix

# before (empty block that breaks the fixer)
with open('a') as f:
    pass
# after
with open('a') as f:
    handle(f)
Defensive patterns

Strategy: fallback

Validate before calling

// Before relying on SIM117 autofix, verify each `with` body has at least one statement:
import ast
for node in ast.walk(ast.parse(src)):
    if isinstance(node, ast.With) and not node.body:
        print('empty with body at line', node.lineno)

Prevention

When it happens

Trigger: The `fix_multiple_with_statements` fixer ran on code where `match_indented_block` returned an `IndentedBlock` whose `body` is empty — i.e. a `with` (or enclosing def) whose block has zero statements after matching. This occurs on syntactically degenerate or exotic trees (empty blocks, dedented constructs, or partial parses) rather than on normal nested-with code.

Common situations: Malformed or auto-generated code fed through `ruff --fix`, a syntax construct the rule's AST matching didn't anticipate, or running an unreleased Ruff build over a corpus of unusual Python files.

Related errors


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