{"record":{"id":"7e4f382ccea8fc1d","repo":"astral-sh/ruff","slug":"failed-to-collapse-if-err","errorCode":null,"errorMessage":"Failed to collapse `if`: {err}","messagePattern":"Failed to collapse `if`: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"info","filePath":"crates/ruff_linter/src/rules/flake8_simplify/rules/collapsible_if.rs","lineNumber":163,"sourceCode":"                            (&nested_if).into(),\n                            checker.locator(),\n                            checker.settings().pycodestyle.max_line_length,\n                            checker.settings().tab_size,\n                        )\n                    }) {\n                        Ok(Some(Fix::applicable_edit(\n                            edit,\n                            if is_collapsible_if_fix_safe_enabled(checker.settings()) {\n                                Safe\n                            } else {\n                                Unsafe\n                            },\n                        )))\n                    } else {\n                        Ok(None)\n                    }\n                }\n                Err(err) => bail!(\"Failed to collapse `if`: {err}\"),\n            }\n        });\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\npub(super) enum NestedIf<'a> {\n    If(&'a ast::StmtIf),\n    Elif(&'a ElifElseClause),\n}\n\nimpl<'a> NestedIf<'a> {\n    fn body(self) -> &'a [Stmt] {\n        match self {\n            NestedIf::If(stmt_if) => &stmt_if.body,\n            NestedIf::Elif(clause) => &clause.body,\n        }\n    }","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/astral-sh/ruff/blob/15f3fe6b15a5f00172f34b0f542f8ea277f5a586/crates/ruff_linter/src/rules/flake8_simplify/rules/collapsible_if.rs#L145-L181","documentation":"Rule SIM102 (collapsible nested `if`) attempts to rewrite `if a: if b:` into `if a and b:` using a libcst-native transformer; if that transformer returns an error, the check bails with this message wrapping the inner error. The diagnostic remains, only the autofix is lost.","triggerScenarios":"`ruff check --fix` on a nested `if` (including `elif` forms) whose libcst round-trip fails — e.g. the outer body contains comments or structure the collapse transformer rejects.","commonSituations":"Nested ifs with `elif` chains, comments between the two `if` headers, or unusual indentation that the libcst collapse can't normalize.","solutions":["Manually combine the conditions: replace `if a: if b:` with `if a and b:` and dedent the body, then rerun ruff","Read the wrapped `{err}` detail to find the libcst failure and adjust the source (e.g. move comments) so the fix applies","Add `# noqa: SIM102` if the nesting is intentional (e.g. readability or short-circuit side effects)"],"exampleFix":"// before\nif a:\n    if b:\n        do()\n// after\nif a and b:\n    do()","handlingStrategy":"try-catch","validationCode":"# only simple two-level nesting is fixable\npython - <<'EOF'\nimport ast, sys\ntree = ast.parse(open(sys.argv[1]).read())\nfor n in ast.walk(tree):\n    if isinstance(n, ast.If) and len(n.body) == 1 and isinstance(n.body[0], ast.If) and not n.orelse and not n.body[0].orelse:\n        print('SIM102 candidate at line', n.lineno)\nEOF","typeGuard":null,"tryCatchPattern":"ruff check --select SIM102 --fix . 2>&1 | tee fix.log; grep -q 'Failed to collapse' fix.log && echo 'collapse failed: merge conditions manually'","preventionTips":["Prefer flat boolean conditions (`if a and b:`) over nested guards while writing code","Avoid comments and stray statements between the two `if` headers","Run the formatter/linter early so nesting never accumulates in generated code"],"tags":["ruff","flake8-simplify","sim102","autofix"],"backgroundTag":"linter-autofix-skipped","analyzedSha":"15f3fe6b15a5f00172f34b0f542f8ea277f5a586","analyzedAt":"2026-09-05T10:32:37.492Z","contentChangedAt":"2026-09-05T10:32:37.492Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}