{"record":{"id":"7e9dcd5e1ab09cc2","repo":"podman-container-tools/podman","slug":"else-without-if","errorCode":null,"errorMessage":"`else` without `if`","messagePattern":"`else` without `if`","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"hack/markdown-preprocess","lineNumber":84,"sourceCode":"            return bool(get_variable(name))\n\n        for m in TOK.finditer(text):\n            # write literal up to token\n            literal = text[pos:m.start()]\n            if is_active():\n                out.append(literal)\n            pos = m.end()\n\n            inner = m.group(1).strip()\n\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)","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/podman-container-tools/podman/blob/a2409076ef2fef60ad9ac046375dedc7d9410ef4/hack/markdown-preprocess#L66-L102","documentation":"Raised when render() encounters a '<< else >>' token while the control-block stack is empty. Each render() call processes exactly one file (an option file included via @@option), so an if/else/endif group must be fully balanced within a single file; an 'if' opened in another file does not count. The stack starts empty per file, hence any '<< else >>' seen before a '<< if ... >>' in the same file is fatal.","triggerScenarios":"An options/*.md file contains '<< else >>' with no preceding '<< if VAR >>' in that same file: the '<< if >>' line was deleted or commented out during editing, or the block was split across two option files, or an endif earlier in the file already popped the frame the else belonged to.","commonSituations":"Editing shared option files that are included by many man pages; merging branch text and accidentally removing the if-line; trying to use else in the top-level .md.in file (where render() is never called, tokens pass through and hit replace_type instead).","solutions":["Add the matching '<< if is_quadlet >>' (or '<< if not is_quadlet >>') line before the '<< else >>'","Remove the stray '<< else >>' if the block is no longer conditional","If the if/endif pair lives in a different option file, move the whole if/else/endif group into one file"],"exampleFix":"# before (single options file)\n<< else >>\nQuadlet-specific text\n<< endif >>\n\n# after\n<< if is_quadlet >>\nGeneric text\n<< else >>\nQuadlet-specific text\n<< endif >>","handlingStrategy":"validation","validationCode":"# Simulate the render() control stack before running the preprocessor\nimport 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":["Keep every if/else/endif group inside a single options/*.md file — the stack is per render() call","Add the lint above to the docs CI job so imbalance fails before the real run","When deleting an '<< if >>' line, delete its else/endif in the same edit"],"tags":["markdown","docs","preprocessor","conditionals"],"backgroundTag":null,"analyzedSha":"a2409076ef2fef60ad9ac046375dedc7d9410ef4","analyzedAt":"2026-08-15T15:57:05.625Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}