podman-container-tools/podman · error · ValueError

`end` without `if`

Error message

`end` without `if`

What it means

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.

Source

Thrown at hack/markdown-preprocess:94

            # control blocks
            if inner.startswith("if ") and len(inner[3:].strip().split(" ")) in [1, 2]:
                cond = inner[3:].strip()
                stack.append({"active": is_active() and truthy(cond), "seen_else": False})
                continue
            if inner == "else":
                if not stack:
                    raise ValueError("`else` without `if`")
                frame = stack[-1]
                if frame["seen_else"]:
                    raise ValueError("multiple `else` in the same `if`")
                frame["seen_else"] = True
                parent_active = all(f["active"] for f in stack[:-1])
                frame["active"] = parent_active and not frame["active"]
                continue
            if inner == "endif":
                if not stack:
                    raise ValueError("`end` without `if`")
                stack.pop()
                continue

            # inline "X if cond else Y" ---
            if " if " in inner and " else " in inner:
                try:
                    # split by " if " then " else "
                    then_part, rest = inner.split(" if ", 1)
                    cond, else_part = rest.split(" else ", 1)
                    cond = cond.strip()
                    chosen = then_part if truthy(cond) else else_part
                    if is_active():
                        out.append(chosen.strip().strip("'\""))
                except Exception as e:
                    raise ValueError(f"Invalid inline if/else syntax: {inner}") from e
                continue

            # Unrecognized token (e.g. `<<a|b>>` for replace_type, or `<<subcommand>>`):

View on GitHub (pinned to a2409076ef)

Solutions

  1. Count '<< if ' vs '<< endif >>' occurrences in the file and delete the surplus endif
  2. Verify the opener spelling: tokens must be lowercase 'if '/'else'/'endif' with single spaces
  3. Keep the whole if/endif group inside one options/*.md file

Example fix

# before
<< if is_quadlet >>A<< endif >>
<< endif >>

# after
<< if is_quadlet >>A<< endif >>
Defensive patterns

Strategy: validation

Validate before calling

import re, pathlib
def lint(text, name):
    stack = 0
    for m in re.finditer(r'<<(.*?)>>', text, re.DOTALL):
        inner = m.group(1).strip()
        if inner.startswith('if ') and len(inner[3:].split()) in (1, 2):
            stack += 1
        elif inner == 'else':
            assert stack > 0, f'{name}: `else` without `if` near offset {m.start()}'
        elif inner == 'endif':
            assert stack > 0, f'{name}: `end` without `if` near offset {m.start()}'
            stack -= 1
    assert stack == 0, f'{name}: {stack} unclosed `if` block(s)'
for p in pathlib.Path('docs/source/markdown/options').glob('*.md'):
    lint(p.read_text(), str(p))

Prevention

When it happens

Trigger: 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.

Common situations: 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).

Related errors


AI-assisted analysis of podman-container-tools/podman@a2409076ef (2026-08-15). Data as JSON: /api/errors/45dd919273661a28. Report an issue: GitHub.