tiangolo/fastapi · error · RuntimeError

Release notes already contain a section for {version}

Error message

Release notes already contain a section for {version}

What it means

Raised by update_release_notes() in scripts/prepare_release.py:70 when the release notes already contain a '## X.Y.Z' or '## X.Y.Z (date)' heading for the version being prepared (regex at scripts/prepare_release.py:69). The release process must not create duplicate version sections.

Source

Thrown at scripts/prepare_release.py:70

def update_version_file(content: str, version: str, version_file: Path) -> str:
    current_version = get_current_version(content, version_file)
    if parse_version(version) <= parse_version(current_version):
        raise RuntimeError(
            f"New version {version} must be greater than current version {current_version}"
        )
    return VERSION_PATTERN.sub(f'__version__ = "{version}"', content, count=1)


def update_release_notes(
    content: str, version: str, release_date: date, release_notes_file: Path
) -> str:
    if not content.startswith(RELEASE_NOTES_HEADER):
        raise RuntimeError(
            f"{release_notes_file} must start with {RELEASE_NOTES_HEADER!r}"
        )
    if re.search(rf"^## {re.escape(version)}(?: \([^)]+\))?$", content, re.M):
        raise RuntimeError(f"Release notes already contain a section for {version}")

    latest_header = f"{RELEASE_NOTES_HEADER}{LATEST_CHANGES_HEADER}\n"
    if not content.startswith(latest_header):
        raise RuntimeError(f"{release_notes_file} must start with {latest_header!r}")

    release_header = f"## {version} ({release_date.isoformat()})"
    return content.replace(
        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(

View on GitHub (pinned to 3e8d1526d8)

Solutions

  1. Check if the version was already released: python scripts/prepare_release.py release-notes --version-file ... --release-notes-file ...
  2. If the section is a stale draft, delete it and re-run prepare.
  3. If the version is genuinely already released, skip prepare for that version.

Example fix

<!-- before: duplicate 1.2.3 section -->
## 1.2.3 (2026-01-01)
- first

## 1.2.3 (2026-01-02)
- second
<!-- after: keep only one -->
## 1.2.3 (2026-01-01)
- first
- second
Defensive patterns

Strategy: validation

Validate before calling

import re
from pathlib import Path

def version_section_exists(path: Path, version: str) -> bool:
    pat = re.compile(rf"(?m)^## {re.escape(version)}(?: \([^)]+\))?$")
    return pat.search(path.read_text(encoding="utf-8")) is not None

Try / catch

try:
    updated = update_release_notes(content, version, d, release_notes_file)
except RuntimeError as e:
    if "already contain a section" in str(e):
        raise SystemExit(f"Version {version} already released: {e}") from e
    raise

Prevention

When it happens

Trigger: Running prepare twice for the same version, or for a version whose section was already added manually. The VERSION_HEADING_PATTERN detects an existing heading identical to the target version.

Common situations: A previous prepare run partially completed (section added, version file not bumped). Re-running a CI job that re-triggers prepare. Manual draft of the section before the tool runs.

Related errors


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