tiangolo/fastapi · error · ValueError
Invalid version: {version!r}. Expected format: X.Y.Z
Error message
Invalid version: {version!r}. Expected format: X.Y.Z What it means
Raised by parse_version() in scripts/prepare_release.py:29 when the version string does not fully match the regex \d+\.\d+\.\d+ (re.fullmatch at scripts/prepare_release.py:27). Every version handled by the release tool — bump, current, target — must be strict semantic X.Y.Z with no leading 'v', no pre-release suffix, and no extra components.
Source
Thrown at scripts/prepare_release.py:29
VERSION_HEADING_PATTERN = re.compile(r"(?m)^## (\d+\.\d+\.\d+)(?: \([^)]+\))?$")
RELEASE_NOTES_HEADER = """---
hide:
- navigation
---
# Release Notes
"""
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"View on GitHub (pinned to 3e8d1526d8)
Solutions
- Normalize the version to exactly three dot-separated integers, e.g. '1.2.3'.
- Strip a leading 'v' and any pre-release suffix before parsing.
- Update __version__ in the version file to plain X.Y.Z before running prepare.
Example fix
# before __version__ = "v1.2.3-rc1" # after __version__ = "1.2.3"
Defensive patterns
Strategy: validation
Validate before calling
import re
def is_valid_version(v: str) -> bool:
return re.fullmatch(r"\d+\.\d+\.\d+", v) is not None Type guard
import re
def is_semver(v: str) -> bool:
return isinstance(v, str) and re.fullmatch(r"\d+\.\d+\.\d+", v) is not None Try / catch
try:
major, minor, patch = parse_version(version)
except ValueError as e:
raise SystemExit(f"Rejecting malformed version: {e}") from e Prevention
- Keep __version__ strictly X.Y.Z with no prefix or pre-release suffix.
- Validate version strings at the CLI boundary before passing to parse_version.
- Reject leading 'v' in inputs.
When it happens
Trigger: Calling prepare with a malformed version string passed into parse_version, or a version_file whose __version__ assignment contains a non-conforming value (e.g. '1.2', 'v1.2.3', '1.2.3.0', '1.2.3-rc1'). bump_version() also routes through parse_version.
Common situations: Reading a __version__ that includes a pre-release tag. Manual CLI argument with a leading 'v'. A version file that uses a 4-segment calver scheme.
Related errors
- Expected exactly one __version__ assignment in {version_file
- New version {version} must be greater than current version {
- Release notes already contain a section for {version}
- {release_notes_file} must start with {RELEASE_NOTES_HEADER!r
- {release_notes_file} must start with {latest_header!r}
AI-assisted analysis of tiangolo/fastapi@3e8d1526d8 (2026-08-11).
Data as JSON: /api/errors/1b4a1ffbd2d00e2a.
Report an issue: GitHub.