{"record":{"id":"9f31d14dd1e3f328","repo":"astral-sh/ruff","slug":"cannot-find-in-else-statement","errorCode":null,"errorMessage":"Cannot find `:` in `else` statement","messagePattern":"Cannot find `:` in `else` statement","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/ruff_linter/src/rules/flake8_return/rules/function.rs","lineNumber":836,"sourceCode":"    if elif_else.test.is_some() {\n        // Ex) `elif` -> `if`\n        Ok(Fix::safe_edit(Edit::deletion(\n            elif_else.start(),\n            elif_else.start() + TextSize::from(2),\n        )))\n    } else {\n        // the start of the line where the `else`` is\n        let else_line_start = locator.line_start(elif_else.start());\n\n        // making a tokenizer to find the Colon for the `else`, not always on the same line!\n        let mut else_line_tokenizer =\n            SimpleTokenizer::starts_at(elif_else.start(), locator.contents());\n\n        // find the Colon for the `else`\n        let Some(else_colon) =\n            else_line_tokenizer.find(|token| token.kind == SimpleTokenKind::Colon)\n        else {\n            return Err(anyhow::anyhow!(\"Cannot find `:` in `else` statement\"));\n        };\n\n        // get the indentation of the `else`, since that is the indent level we want to end with\n        let Some(desired_indentation) = indentation(locator.contents(), elif_else) else {\n            return Err(anyhow::anyhow!(\"Compound statement cannot be inlined\"));\n        };\n\n        // If the statement is on the same line as the `else`, just remove the `else: `.\n        // Ex) `else: return True` -> `return True`\n        if let Some(first) = elif_else.body.first() {\n            if indexer.preceded_by_multi_statement_line(first, locator.contents()) {\n                return Ok(Fix::safe_edit(Edit::deletion(\n                    elif_else.start(),\n                    first.start(),\n                )));\n            }\n        }\n","sourceCodeStart":818,"sourceCodeEnd":854,"githubUrl":"https://github.com/astral-sh/ruff/blob/26f38c119cac42e4d320ba08f09224fdec74af2c/crates/ruff_linter/src/rules/flake8_return/rules/function.rs#L818-L854","documentation":"Internal guard in Ruff's fixer for flake8-return rules (RET505 `superfluous-else-return` and siblings). When inlining an `else:` clause, `remove_else` tokenizes source starting at the `else` keyword to locate the colon that terminates the `else` header. If the tokenizer reaches the end of the token stream without a `Colon`, fix construction is aborted; the diagnostic is still reported, just without an autofix.","triggerScenarios":"`remove_else` (via `superfluous_else_node`) invoked on an `else` clause whose header colon cannot be found by `SimpleTokenizer::starts_at(elif_else.start(), ...)` — e.g. unusual source layout where `else` is not followed by a colon token in the linear stream.","commonSituations":"Applying `ruff --fix` to RET505-RET508 hits with exotic but parseable formatting or generated code; a Ruff parser/tokenizer or `ElifElseClause` range edge case; `else` keywords in unexpected positions such as line continuations.","solutions":["Reformat the `if/else` normally (colon directly after `else`) and re-run `ruff --fix`.","Minimize with `ruff check --isolated --fix path/to/file.py`; if it persists on valid input, file a bug at https://github.com/astral-sh/ruff — this is an internal invariant failure, not user error.","Work around by disabling the triggering RET rule (e.g. `lint.ignore = [\"RET505\"]`) or fixing the code manually.","Update Ruff to the latest version; tokenizer edge cases are fixed regularly."],"exampleFix":"// before (edge-case layout that stalls the fixer)\nif cond:\n    return a\nelse \\\n    : return b\n// after (normal layout the fixer handles)\nif cond:\n    return a\nelse:\n    return b","handlingStrategy":"try-catch","validationCode":"import re\n\ndef else_clause_is_fixable(source: str) -> bool:\n    # a normal `else:` header on its own line is safe to autofix\n    return bool(re.search(r'^[ \\t]*else[ \\t]*:[ \\t]*', source, re.MULTILINE))","typeGuard":"def has_plain_else_colon(source: str) -> bool:\n    return any(line.lstrip().startswith('else:') for line in source.splitlines())","tryCatchPattern":"try:\n    subprocess.run(['ruff', 'check', '--fix', '--select', 'RET505', path], check=True)\nexcept subprocess.CalledProcessError:\n    print(f'autofix failed for {path}; reformat else: headers manually and retry')","preventionTips":["Keep `else:` headers on their own line with the colon immediately after the keyword.","Run `ruff check --no-fix` first; report any RET505 hit without a fix upstream as a bug.","Pin and regularly update Ruff; tokenizer edge cases get fixed over time.","Scope RET505-RET508 autofixes to directories with conventionally formatted code."],"tags":["linter","autofix","rust","tokenizer","flake8-return"],"backgroundTag":"autofix-failed","analyzedSha":"26f38c119cac42e4d320ba08f09224fdec74af2c","analyzedAt":"2026-09-05T10:32:37.492Z","contentChangedAt":"2026-09-05T10:32:37.492Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}