tiangolo/fastapi · error · RuntimeError

Release notes section for {version} in {release_notes_file}

Error message

Release notes section for {version} in {release_notes_file} is empty

What it means

Raised by get_release_notes_body() in scripts/prepare_release.py:96 when the version section heading exists but its body (text up to the next '## ' heading, stripped) is empty (scripts/prepare_release.py:94-95). The release-notes command is meant to output the changelog body for tooling, so an empty body is treated as an error rather than emitting nothing.

Source

Thrown at scripts/prepare_release.py:96

        latest_header,
        f"{RELEASE_NOTES_HEADER}{LATEST_CHANGES_HEADER}\n\n{release_header}\n",
        1,
    )


def get_release_notes_body(content: str, version: str, release_notes_file: Path) -> str:
    version_heading = re.compile(rf"(?m)^## {re.escape(version)}(?: \([^)]+\))?$")
    match = version_heading.search(content)
    if not match:
        raise RuntimeError(
            f"Could not find release notes section for {version} in {release_notes_file}"
        )

    next_match = VERSION_HEADING_PATTERN.search(content, match.end())
    end = next_match.start() if next_match else len(content)
    body = content[match.end() : end].strip()
    if not body:
        raise RuntimeError(
            f"Release notes section for {version} in {release_notes_file} is empty"
        )
    return f"{body}\n"


@app.command()
def prepare(
    bump: Annotated[
        BumpType,
        typer.Argument(
            envvar="PREPARE_RELEASE_BUMP",
            help="The release bump to make: major, minor, or patch.",
        ),
    ],
    version_file: Annotated[
        Path,
        typer.Option(
            envvar="PREPARE_RELEASE_VERSION_FILE",

View on GitHub (pinned to 3e8d1526d8)

Solutions

  1. Populate the section with at least one changelog entry under the '## <version>' heading.
  2. Remove the empty heading if the version is not being released.
  3. Re-run release-notes once the section has content.

Example fix

<!-- before -->
## 1.2.3 (2026-01-01)

## 1.2.2 (2025-12-01)
<!-- after -->
## 1.2.3 (2026-01-01)
- Fixed login redirect

## 1.2.2 (2025-12-01)
Defensive patterns

Strategy: validation

Validate before calling

import re
from pathlib import Path

def section_has_body(path: Path, version: str) -> bool:
    text = path.read_text(encoding="utf-8")
    m = re.search(rf"(?m)^## {re.escape(version)}(?: \([^)]+\))?$", text)
    if not m:
        return False
    nxt = re.search(r"(?m)^## ", text[m.end():])
    body = text[m.end(): m.end() + (nxt.start() if nxt else len(text) - m.end())].strip()
    return bool(body)

Try / catch

try:
    body = get_release_notes_body(content, version, release_notes_file)
except RuntimeError as e:
    if "is empty" in str(e):
        raise SystemExit(f"Add changelog entries under ## {version}") from e
    raise

Prevention

When it happens

Trigger: Running `release-notes` for a version whose '## X.Y.Z' heading was added but contains no bullet points (e.g. a placeholder section), or where all content between headings is whitespace.

Common situations: prepare created the heading but no PR descriptions were added under it. A maintainer added the heading as a stub. Content was accidentally deleted leaving the heading orphaned.

Related errors


AI-assisted analysis of tiangolo/fastapi@3e8d1526d8 (2026-08-11). Data as JSON: /api/errors/501988be351de24f. Report an issue: GitHub.