podman-container-tools/podman · error · ValueError

unclosed `if` block(s)

Error message

unclosed `if` block(s)

What it means

Raised at the end of render() when the control-block stack is still non-empty: at least one '<< if ... >>' was never closed with '<< endif >>'. Unclosed blocks also silently swallow the file's trailing literal (it is only appended when is_active()). A typo'd closer such as '<< end >>' or '<< ENDIF >>' is not recognized (it is preserved verbatim as an unrecognized token) and leaves the frame open, producing this error at EOF.

Source

Thrown at hack/markdown-preprocess:122

                    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>>`):
            # preserve verbatim so downstream processing can handle it.
            if is_active():
                out.append(m.group(0))

        # trailing literal
        if is_active():
            out.append(text[pos:])

        if stack:
            raise ValueError("unclosed `if` block(s)")
        return "".join(out)

    def process(self, infile:str):
        """
        Main calling point: preprocesses one file
        """
        self.infile = infile
        # Some options are the same between containers and pods; determine
        # which description to use from the name of the source man page.
        self.pod_or_container = 'container'
        if '-pod-' in infile or '-kube-' in infile:
            self.pod_or_container = 'pod'

        # foo.md.in -> foo.md -- but always write to a tmpfile
        outfile = os.path.splitext(infile)[0]
        outfile_tmp = outfile + '.tmp.' + str(os.getpid())

        with open(infile, 'r', encoding='utf-8') as fh_in, open(outfile_tmp, 'w', encoding='utf-8', newline='\n') as fh_out:

View on GitHub (pinned to a2409076ef)

Solutions

  1. Add one '<< endif >>' per open '<< if >>' at the end of the block
  2. Check the closer's exact spelling: lowercase 'endif' with single spaces inside << >>
  3. grep -c '<< if ' and '<< endif >>' in the file and make the counts match

Example fix

# before
<< if is_quadlet >>
**--pod**=...

# after
<< if is_quadlet >>
**--pod**=...
<< 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`'
        elif inner == 'endif':
            assert stack > 0, f'{name}: `end` without `if`'
            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 options/*.md file ends with '<< if is_quadlet >>' ... and no '<< endif >>'; or the closer is misspelled ('<< end >>', '<< fi >>', '<<Endif>>'), so the stack never empties.

Common situations: Appending new conditional text at the bottom of a shared option file and forgetting the closer; renaming/reformatting tokens with an editor that changed case; block accidentally spanning past end of file during a merge conflict resolution.

Related errors


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