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
- Normalize indentation in the file (consistent spaces) and rerun `ruff check --fix`
- Merge the consecutive `with` statements manually into `with A as a, B as b:`
- 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
- Write `with A as a, B as b:` directly instead of stacking `with` blocks
- Run ruff format before ruff check --fix so indentation heuristics succeed
- Keep parenthesized multiline `with` headers in a consistent style
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
- Failed to collapse `with`: {err}
- Expected indented block to have at least one statement
- Expected outer with to have indented body
- Expected one inner with statement
- Expected colon after test
AI-assisted analysis of astral-sh/ruff@26f38c119c (2026-09-05).
Data as JSON: /api/errors/9b33a90e3c9c65f3.
Report an issue: GitHub.