tiangolo/fastapi · error · ValueError
Invalid ID format, it must start with "isbn-" or "imdb-"
Error message
Invalid ID format, it must start with "isbn-" or "imdb-"
What it means
A ValueError raised inside an AfterValidator (check_valid_id) attached to the `id` query parameter. FastAPI/Pydantic converts validator ValueErrors into a 422 RequestValidationError response. The validator requires the id to start with 'isbn-' or 'imdb-'. This shows constraining query-string format via Pydantic AfterValidator.
Solutions
- Prefix the id correctly, e.g. GET /items/?id=isbn-9781529046137 or GET /items/?id=imdb-tt0371724.
- Drop the id parameter to get a random valid item.
- If new prefixes should be allowed, update the tuple passed to str.startswith.
Example fix
// before
def check_valid_id(id: str):
if not id.startswith(("isbn-", "imdb-")):
raise ValueError('Invalid ID format, it must start with "isbn-" or "imdb-"')
return id
// after (add prefix and clearer message)
ALLOWED = ("isbn-", "imdb-")
def check_valid_id(id: str):
if not id.startswith(ALLOWED):
raise ValueError(f'Invalid ID format, it must start with one of {ALLOWED}')
return id Defensive patterns
Strategy: validation
Validate before calling
def check_valid_id(id: str):
if not id.startswith(('isbn-', 'imdb-')):
raise ValueError('bad id')
check_valid_id(id) # raises locally before the request Type guard
def is_valid_id(id: str) -> bool:
return id.startswith(('isbn-', 'imdb-')) Try / catch
resp = requests.get('http://localhost:8000/items/', params={'id': id})
if resp.status_code == 422:
print('Invalid id format:', resp.json()['detail']) Prevention
- Always prefix ids with isbn- or imdb-.
- Omit id to get a random valid item.
- Update the allowed-prefix tuple when new namespaces are added.
When it happens
Trigger: GET /items/?id=<value> where <value> does not start with 'isbn-' or 'imdb-'. Omitting id entirely is allowed (defaults to None, then random).
Common situations: Enforcing ID prefixes / formats on query params. Developers hit 422 when they pass a raw id without the required prefix or with wrong casing.
Related errors
- {"errors": exc.errors(), "body": body.decode()}
- {"errors": exc.errors(), "body": body.decode()}
- Expected UploadFile, received
- Invalid YAML
- validation error(s)
AI-assisted analysis of tiangolo/fastapi@3e8d1526d8 (2026-08-11).
Data as JSON: /api/errors/9ef8f6ae1df13759.
Report an issue: GitHub.
Appendix: source
Thrown at docs_src/query_params_str_validations/tutorial015_an_py310.py:18
import random
from typing import Annotated
from fastapi import FastAPI
from pydantic import AfterValidator
app = FastAPI()
data = {
"isbn-9781529046137": "The Hitchhiker's Guide to the Galaxy",
"imdb-tt0371724": "The Hitchhiker's Guide to the Galaxy",
"isbn-9781439512982": "Isaac Asimov: The Complete Stories, Vol. 2",
}
def check_valid_id(id: str):
if not id.startswith(("isbn-", "imdb-")):
raise ValueError('Invalid ID format, it must start with "isbn-" or "imdb-"')
return id
@app.get("/items/")
async def read_items(
id: Annotated[str | None, AfterValidator(check_valid_id)] = None,
):
if id:
item = data.get(id)
else:
id, item = random.choice(list(data.items()))
return {"id": id, "name": item}
View on GitHub (pinned to 3e8d1526d8)