tiangolo/fastapi · error · RuntimeError

Expected exactly one __version__ assignment in {version_file

Error message

Expected exactly one __version__ assignment in {version_file}, found {len(matches)}

What it means

Raised by get_current_version() in scripts/prepare_release.py:37 when VERSION_PATTERN (a multiline regex for exactly `__version__ = "X.Y.Z"`) does not match exactly once in the version file. The release tool needs an unambiguous single source of truth for the current version; zero matches means it cannot find it, two or more means it cannot decide which is authoritative.

Source

Thrown at scripts/prepare_release.py:37

"""
LATEST_CHANGES_HEADER = "## Latest Changes"
BumpType = Literal["major", "minor", "patch"]

app = typer.Typer()


def parse_version(version: str) -> tuple[int, int, int]:
    match = re.fullmatch(r"\d+\.\d+\.\d+", version)
    if not match:
        raise ValueError(f"Invalid version: {version!r}. Expected format: X.Y.Z")
    major, minor, patch = version.split(".")
    return int(major), int(minor), int(patch)


def get_current_version(content: str, version_file: Path) -> str:
    matches = list(VERSION_PATTERN.finditer(content))
    if len(matches) != 1:
        raise RuntimeError(
            f"Expected exactly one __version__ assignment in {version_file}, "
            f"found {len(matches)}"
        )
    return matches[0].group(1)


def bump_version(version: str, bump: BumpType) -> str:
    major, minor, patch = parse_version(version)
    if bump == "major":
        return f"{major + 1}.0.0"
    if bump == "minor":
        return f"{major}.{minor + 1}.0"
    return f"{major}.{minor}.{patch + 1}"


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):

View on GitHub (pinned to 3e8d1526d8)

Solutions

  1. Ensure the version file contains exactly one line of the form `__version__ = "X.Y.Z"` with double quotes.
  2. Remove any duplicate __version__ assignments.
  3. Point --version-file at the file that is the canonical version source (e.g. fastapi/__init__.py).

Example fix

# before (two assignments)
__version__ = "0.1.0"
__version__ = "0.2.0"
# after (exactly one)
__version__ = "0.2.0"
Defensive patterns

Strategy: validation

Validate before calling

import re
from pathlib import Path

VERSION_PATTERN = re.compile(r'(?m)^__version__ = "(\d+\.\d+\.\d+)"$')

def has_single_version(path: Path) -> bool:
    return len(VERSION_PATTERN.findall(path.read_text())) == 1

Try / catch

try:
    current = get_current_version(content, version_file)
except RuntimeError as e:
    raise SystemExit(f"Version file is ambiguous: {e}") from e

Prevention

When it happens

Trigger: Calling prepare / current-version / release-notes. VERSION_PATTERN.finditer returns 0 (no __version__ assignment, or one with different quoting/spacing) or >1 (multiple assignments, e.g. a module-level constant and a duplicated line).

Common situations: The version file was refactored and __version__ moved into __init__.py elsewhere. Quoting style changed from double to single quotes. A second __version__ assignment was added conditionally. The path points at the wrong file.

Related errors


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