{"record":{"id":"12d84fce5990b78e","repo":"unslothai/unsloth","slug":"invalid-version","errorCode":null,"errorMessage":"Invalid version.","messagePattern":"Invalid version\\.","errorType":"http","errorClass":"HTTPException","httpStatus":422,"severity":"warning","filePath":"studio/backend/main.py","lineNumber":1746,"sourceCode":"    \"\"\"Return source-aware install metadata without remote update checks.\"\"\"\n    return get_studio_install_source_status(UNSLOTH_VERSION)\n\n\n@app.get(\"/api/studio/update-status\")\ndef studio_update_status(_current_subject: str = Depends(get_current_subject)):\n    \"\"\"Return source-aware manual update status for browser-served Unsloth.\"\"\"\n    return get_studio_update_status(UNSLOTH_VERSION)\n\n\n@app.get(\"/api/studio/release-notes\")\ndef studio_release_notes(\n    version: str = Query(..., max_length = 64),\n    refresh: bool = Query(False),\n    _current_subject: str = Depends(get_current_subject),\n):\n    \"\"\"Return the newest release's notes. `version` is echoed, not looked up.\"\"\"\n    if not is_supported_version_query(version):\n        raise HTTPException(status_code = 422, detail = \"Invalid version.\")\n    return get_release_notes(version, refresh = refresh)\n\n\n@app.get(\n    \"/api/studio/download-transport-capabilities\",\n    response_model = TransportCapabilities,\n)\ndef studio_download_transport_capabilities(\n    probe: bool = False, _current_subject: str = Depends(get_current_subject)\n):\n    # Sync def, so FastAPI runs this in the threadpool and an opted-in probe cannot block the loop.\n    return asdict(get_download_transport_capabilities(probe = probe))\n\n\n@app.post(\"/api/shutdown\")\nasync def shutdown_server(request: Request, current_subject: str = Depends(get_current_subject)):\n    \"\"\"Gracefully shut down the Unsloth Studio server.\n","sourceCodeStart":1728,"sourceCodeEnd":1764,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/main.py#L1728-L1764","documentation":"HTTP 422 from GET /api/studio/release-notes when the 'version' query parameter fails is_supported_version_query(). That helper requires the string to match a safe version pattern and parse as a real version — 'latest', 'main', path-like strings, or garbage are rejected. The version is only echoed back (not used to look up a release), so this is purely shape validation to let the UI drop stale responses.","triggerScenarios":"Calling /api/studio/release-notes?version=latest, version=main, version=v (unparseable), a path like '../../etc', or any string with characters outside _SAFE_VERSION_PATTERN or longer than the 64-char Query cap.","commonSituations":"A frontend change that passes a branch name or 'latest' instead of a semver string; manual curl testing with placeholder values; a client built against an older API that accepted arbitrary strings.","solutions":["Pass a concrete version string shaped like a version (e.g. version=2025.8.1 or version=v1.2.3) that the update popup would actually offer.","Do not send 'latest'/'main' — fetch the update status endpoint first and use the version it reports.","If you control the client, validate the version before sending (regex for digits/dots/optional v-prefix)."],"exampleFix":"# before\nGET /api/studio/release-notes?version=latest   # 422\n# after\nGET /api/studio/release-notes?version=2025.8.1  # 200","handlingStrategy":"validation","validationCode":"import re\n\n_VERSION_RE = re.compile(r\"^v?\\d+(\\.\\d+)*(-[A-Za-z0-9.]+)?$\")\n\ndef is_supported_version_query(version: str) -> bool:\n    c = version.strip()\n    return len(c) <= 64 and bool(_VERSION_RE.match(c)) and c.lower() not in {\"latest\", \"main\"}","typeGuard":"def is_version_query(v: str) -> bool:\n    \"\"\"Narrow a string to a version the release-notes endpoint accepts.\"\"\"\n    return isinstance(v, str) and is_supported_version_query(v)","tryCatchPattern":null,"preventionTips":["Always derive the version from the update-status response instead of hardcoding 'latest'.","Validate version strings client-side with a strict regex before querying.","Remember the value is echoed, not looked up — only send versions the UI actually offers."],"tags":["api","validation","http-422","release-notes"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}