astral-sh/ruff · info

Unable to fix multiline statement

Error message

Unable to fix multiline statement

What it means

`fix_multiple_with_statements` (the SIM117 collapse of consecutive `with` statements) begins by inferring the indentation of the `with` statement via `whitespace::indentation`; if none can be inferred it aborts with this message rather than emit a wrongly indented merged `with`. Like the other flake8-simplify bails, the diagnostic still fires and only the autofix is skipped.

Source

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

use ruff_python_ast::whitespace;
use ruff_python_codegen::Stylist;
use ruff_source_file::LineRanges;
use ruff_text_size::Ranged;

use crate::Edit;
use crate::Locator;
use crate::cst::matchers::{match_function_def, match_indented_block, match_statement, match_with};
use crate::fix::codemods::CodegenStylist;

/// (SIM117) Convert `with a: with b:` to `with a, b:`.
pub(crate) fn fix_multiple_with_statements(
    locator: &Locator,
    stylist: &Stylist,
    with_stmt: &ast::StmtWith,
) -> Result<Edit> {
    // Infer the indentation of the outer block.
    let Some(outer_indent) = whitespace::indentation(locator.contents(), with_stmt) else {
        bail!("Unable to fix multiline statement");
    };

    // Extract the module text.
    let contents = locator.lines_str(with_stmt.range());

    // If the block is indented, "embed" it in a function definition, to preserve
    // indentation while retaining valid source code. (We'll strip the prefix later
    // on.)
    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() {

View on GitHub (pinned to 26f38c119c)

Solutions

  1. Normalize indentation in the file (consistent spaces) and rerun `ruff check --fix`
  2. Merge the consecutive `with` statements manually into `with A as a, B as b:`
  3. Suppress with `# noqa: SIM117` when separate `with` blocks are deliberate

Example fix

// before
with open(a) as f:
    with open(b) as g:
        data = f.read() + g.read()
// after
with open(a) as f, open(b) as g:
    data = f.read() + g.read()
Defensive patterns

Strategy: validation

Validate before calling

# ensure indentation is inferable (spaces only, standard 4-space blocks) before SIM117 fix
python - <<'EOF'
import sys
src = open(sys.argv[1]).read()
assert '\t' not in src, 'normalize tabs to spaces first'
EOF

Try / catch

ruff check --select SIM117 --fix . || python - <<'EOF'
print('SIM117 fix skipped: collapse the with statements manually')
EOF

Prevention

When it happens

Trigger: `ruff check --fix` on consecutive `with` statements (SIM117) whose indentation cannot be derived — e.g. module-level multiline `with` headers, tabs/space mixtures, or statements whose range spans odd line layouts.

Common situations: Multiline parenthesized `with` headers, files reformatted with different indent styles, generated code blocks lacking normal block indentation.

Related errors


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