{"record":{"id":"45dd919273661a28","repo":"podman-container-tools/podman","slug":"end-without-if","errorCode":null,"errorMessage":"`end` without `if`","messagePattern":"`end` without `if`","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"hack/markdown-preprocess","lineNumber":94,"sourceCode":"\n            # control blocks\n            if inner.startswith(\"if \") and len(inner[3:].strip().split(\" \")) in [1, 2]:\n                cond = inner[3:].strip()\n                stack.append({\"active\": is_active() and truthy(cond), \"seen_else\": False})\n                continue\n            if inner == \"else\":\n                if not stack:\n                    raise ValueError(\"`else` without `if`\")\n                frame = stack[-1]\n                if frame[\"seen_else\"]:\n                    raise ValueError(\"multiple `else` in the same `if`\")\n                frame[\"seen_else\"] = True\n                parent_active = all(f[\"active\"] for f in stack[:-1])\n                frame[\"active\"] = parent_active and not frame[\"active\"]\n                continue\n            if inner == \"endif\":\n                if not stack:\n                    raise ValueError(\"`end` without `if`\")\n                stack.pop()\n                continue\n\n            # inline \"X if cond else Y\" ---\n            if \" if \" in inner and \" else \" in inner:\n                try:\n                    # split by \" if \" then \" else \"\n                    then_part, rest = inner.split(\" if \", 1)\n                    cond, else_part = rest.split(\" else \", 1)\n                    cond = cond.strip()\n                    chosen = then_part if truthy(cond) else else_part\n                    if is_active():\n                        out.append(chosen.strip().strip(\"'\\\"\"))\n                except Exception as e:\n                    raise ValueError(f\"Invalid inline if/else syntax: {inner}\") from e\n                continue\n\n            # Unrecognized token (e.g. `<<a|b>>` for replace_type, or `<<subcommand>>`):","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/podman-container-tools/podman/blob/a2409076ef2fef60ad9ac046375dedc7d9410ef4/hack/markdown-preprocess#L76-L112","documentation":"Raised when a '<< endif >>' token arrives with an empty stack — i.e. there is no open '<< if >>' to close in this file. The message text says 'end' but the token is literally 'endif'. Like the other control-flow errors, the if/endif pair must balance within a single rendered file (one options/*.md), because the stack is per-render() call.","triggerScenarios":"An extra '<< endif >>' left after deleting an '<< if >>' line; a typo'd opener that is not recognized ('<< IF is_quadlet >>', '<<if is_quadlet>>' without the space after 'if' fails startswith('if ')... note '<<if ...>>' has inner='if is_quadlet' which does start with 'if ' so only case variants break it; '<< endif >>' after the block was already closed; if opened in one option file and endif placed in another.","commonSituations":"Deleting or commenting out an if-line while leaving its endif; splitting conditional content between two option files or between the option file and the .md.in man page; uppercase '<< ENDIF >>' (not recognized as closer, then the real extra endif triggers this).","solutions":["Count '<< if ' vs '<< endif >>' occurrences in the file and delete the surplus endif","Verify the opener spelling: tokens must be lowercase 'if '/'else'/'endif' with single spaces","Keep the whole if/endif group inside one options/*.md file"],"exampleFix":"# before\n<< if is_quadlet >>A<< endif >>\n<< endif >>\n\n# after\n<< if is_quadlet >>A<< endif >>","handlingStrategy":"validation","validationCode":"import re, pathlib\ndef lint(text, name):\n    stack = 0\n    for m in re.finditer(r'<<(.*?)>>', text, re.DOTALL):\n        inner = m.group(1).strip()\n        if inner.startswith('if ') and len(inner[3:].split()) in (1, 2):\n            stack += 1\n        elif inner == 'else':\n            assert stack > 0, f'{name}: `else` without `if` near offset {m.start()}'\n        elif inner == 'endif':\n            assert stack > 0, f'{name}: `end` without `if` near offset {m.start()}'\n            stack -= 1\n    assert stack == 0, f'{name}: {stack} unclosed `if` block(s)'\nfor p in pathlib.Path('docs/source/markdown/options').glob('*.md'):\n    lint(p.read_text(), str(p))","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Match counts per file: [ \"$(grep -c '<< if ' f)\" -eq \"$(grep -c '<< endif >>' f)\" ]","Tokens are case-sensitive lowercase ('if', 'else', 'endif') — avoid editor auto-capitalization in comments","Keep if/endif pairs within one option file; never split them across the .md.in and the option file"],"tags":["markdown","docs","preprocessor","conditionals"],"backgroundTag":null,"analyzedSha":"a2409076ef2fef60ad9ac046375dedc7d9410ef4","analyzedAt":"2026-08-15T15:57:05.625Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}