ruvnet/RuView · warning · ValueError
Unsupported API version: {version}
Error message
Unsupported API version: {version} What it means
Spec code in plans/phase2-architecture/api-architecture.md: APIVersionManager.get_router(version) raises ValueError for any version not in its registry dict ({'v1': stable, 'v2': beta}). There is no input normalization, so 'V1', 'v1/', ' v1', 'v3', or '' all fail identically. This is the gate that maps an API-version string to the corresponding router module.
Source
Thrown at plans/phase2-architecture/api-architecture.md:346
def __init__(self):
self.versions = {
'v1': {
'status': 'stable',
'deprecated': False,
'sunset_date': None
},
'v2': {
'status': 'beta',
'deprecated': False,
'sunset_date': None
}
}
def get_router(self, version: str):
"""Get router for specific API version"""
if version not in self.versions:
raise ValueError(f"Unsupported API version: {version}")
if version == 'v1':
from .v1 import router as v1_router
return v1_router
elif version == 'v2':
from .v2 import router as v2_router
return v2_router
def check_deprecation(self, version: str):
"""Check if API version is deprecated"""
version_info = self.versions.get(version)
if version_info and version_info['deprecated']:
return {
'deprecated': True,
'sunset_date': version_info['sunset_date'],
'migration_guide': f'/docs/migration/{version}-to-v{int(version[1])+1}'
}View on GitHub (pinned to 4685618388)
Solutions
- Pass exactly 'v1' or 'v2' — the two registered keys
- Normalize before calling: version = version.strip().lower() and strip surrounding slashes
- If you need a new version, add it to the versions registry dict first, then handle it in get_router
- Validate the version at the request boundary and return FastAPI 400/404 rather than letting ValueError escape
Example fix
# before
router = manager.get_router(path_version) # ValueError on 'V1' or 'v1/'
# after
version = path_version.strip("/").strip().lower()
if version not in manager.versions:
raise HTTPException(status_code=404, detail=f"Unsupported API version: {version}")
router = manager.get_router(version) Defensive patterns
Strategy: validation
Validate before calling
SUPPORTED_API_VERSIONS = {"v1", "v2"}
version = raw_version.strip("/").strip().lower()
if version not in SUPPORTED_API_VERSIONS:
raise HTTPException(status_code=404, detail=f"Unsupported API version: {raw_version!r}")
router = manager.get_router(version) Type guard
from typing import Literal
def is_supported_api_version(v: str) -> bool:
return v in {"v1", "v2"}
# after guard, annotate for narrow typing:
def get_router_checked(version: str) -> Literal["v1", "v2"]-routed: ... Try / catch
try:
router = manager.get_router(version)
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e)) from e # client-facing 404, not a 500 Prevention
- Normalize version strings (strip slashes/whitespace, lower-case) at the request boundary
- Expose the supported set from the manager (sorted(manager.versions)) in a discovery endpoint
- Register new versions in the versions dict before shipping clients that use them
When it happens
Trigger: Calling get_router() with a version taken verbatim from a request path/query (e.g. /api/V1/... or /api/v1//pose), a config value with stray whitespace or different casing, or a genuinely new version ('v3') not yet registered.
Common situations: Clients sending uppercase or padded version strings; URL routing that keeps a trailing slash in the version capture; adding a new API version without extending the versions dict; typos in configuration files.
Related errors
- Unknown topic: {topic}
- Page must be >= 1
- Size must be >= 1
- Size must be <= {max_size}
- min_confidence must be between 0.0 and 1.0
AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16).
Data as JSON: /api/errors/1e6309e6ac5e6674.
Report an issue: GitHub.