{"record":{"id":"969886b1e019e097","repo":"firecracker-microvm/firecracker","slug":"version-does-not-match-vx-y-z","errorCode":null,"errorMessage":"version does not match vX.Y.Z","messagePattern":"version does not match vX\\.Y\\.Z","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"tools/gh_release.py","lineNumber":99,"sourceCode":"\n    # Upload assets\n    for asset in assets:\n        content_type = \"application/octet-stream\"\n        if asset.suffix == \".txt\":\n            content_type = \"text/plain\"\n        elif asset.suffix in {\".tgz\", \".gz\"}:\n            content_type = \"application/gzip\"\n        print(f\"Uploading asset {asset} with content-type={content_type}\")\n        gh_release.upload_asset(str(asset), label=asset.name, content_type=content_type)\n\n    release_url = gh_release.html_url\n    print(f\"Draft release created successful. Check it out at {release_url}\")\n\n\ndef version(version_str: str):\n    \"\"\"Validate version parameter\"\"\"\n    if not re.fullmatch(r\"v\\d+\\.\\d+\\.\\d+\", version_str):\n        raise ValueError(\"version does not match vX.Y.Z\")\n    return version_str\n\n\nif __name__ == \"__main__\":\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\n        \"--version\",\n        required=True,\n        metavar=\"vX.Y.Z\",\n        help=\"Firecracker version.\",\n        type=version,\n    )\n    parser.add_argument(\n        \"--repository\", required=False, default=\"firecracker-microvm/firecracker\"\n    )\n    parser.add_argument(\"--github-token\", required=True)\n    args = parser.parse_args()\n    github_release(","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/firecracker-microvm/firecracker/blob/ea50487ec11602100b90ed63f85fe00bd30fbde8/tools/gh_release.py#L81-L117","documentation":"tools/gh_release.py registers `version` as the argparse `type` for the required `--version` flag. The function runs `re.fullmatch(r\"v\\d+\\.\\d+\\.\\d+\", version_str)` and raises ValueError when the string is not exactly a semantic version prefixed with a lowercase 'v'. argparse catches this ValueError and re-emits it as a usage error (`argument --version: invalid version value: ...`), aborting the draft-release/upload workflow before any asset is uploaded.","triggerScenarios":"Calling `python3 tools/gh_release.py --version 1.10.0` (missing the leading 'v'), `--version v1.10` (only two components), `--version v1.10.0-rc1` or `--version v1.10.0^` (pre-release/build suffixes rejected by fullmatch), or `--version V1.10.0` (uppercase V). Any of these makes the release tool exit during argument parsing.","commonSituations":"Release scripts that interpolate a git tag or CARGO_PKG_VERSION into `--version`; git tags carrying an annotated suffix; CI passing the version from a variable that was normalized without the 'v'. The regex demands the literal tag format `vX.Y.Z`, which is stricter than plain semver.","solutions":["Pass the version in exact tag form with the leading lowercase v: `python3 tools/gh_release.py --version v1.10.0`.","Strip pre-release/build metadata before invoking: use the released tag (e.g. v1.10.0) instead of v1.10.0-rc1.","If you drive this from CI, derive the argument from the git tag itself (`git describe --tags --exact-match`) so the format always matches, or add `v${VERSION}` formatting in the pipeline.","Only as a last resort, extend the regex in tools/gh_release.py (e.g. allow `-rc\\d+`) if your project genuinely releases pre-release versions through this tool."],"exampleFix":"# before\npython3 tools/gh_release.py --version 1.10.0   # ValueError: version does not match vX.Y.Z\n\n# after\npython3 tools/gh_release.py --version v1.10.0","handlingStrategy":"validation","validationCode":"import re, subprocess, sys\n\nversion = os.environ[\"RELEASE_VERSION\"]  # however you obtain it\nif not re.fullmatch(r\"v\\d+\\.\\d+\\.\\d+\", version):\n    tag = subprocess.run([\"git\", \"describe\", \"--tags\", \"--exact-match\"],\n                         capture_output=True, text=True).stdout.strip()\n    version = tag if re.fullmatch(r\"v\\d+\\.\\d+\\.\\d+\", tag) else f\"v{version.lstrip('v')}\"\nsubprocess.run([sys.executable, \"tools/gh_release.py\", \"--version\", version])","typeGuard":"def is_release_tag(s: str) -> bool:\n    \"\"\"True when s is exactly the vX.Y.Z form gh_release.py accepts.\"\"\"\n    return re.fullmatch(r\"v\\d+\\.\\d+\\.\\d+\", s) is not None","tryCatchPattern":"# argparse converts the ValueError into a usage error and exits(2); catch SystemExit if wrapping the CLI\ntry:\n    tools.gh_release.main([\"--version\", version])\nexcept SystemExit as e:\n    print(f\"release aborted (bad --version): {version!r}\", file=sys.stderr)\n    raise","preventionTips":["Derive --version from the git tag itself (git describe --tags --exact-match) instead of hand-typing it.","Standardize on the literal v-prefixed tag format vX.Y.Z in CI variables before calling release tooling.","Keep pre-release suffixes out of values passed to this script; strip them in the pipeline."],"tags":["python","argparse","release-tooling","validation","versioning"],"backgroundTag":null,"analyzedSha":"ea50487ec11602100b90ed63f85fe00bd30fbde8","analyzedAt":"2026-08-16T11:52:35.093Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}