tiangolo/fastapi · error · HTTPException
Hero not found
Error message
Hero not found
What it means
Application-defined HTTPException in the read_hero GET endpoint of the multi-model tutorial (Annotated style). After session.get(Hero, hero_id) returns None for a missing primary key, the handler raises HTTPException(status_code=404, detail="Hero not found"); FastAPI turns that into a 404 response with {"detail":"Hero not found"}.
Source
Thrown at docs_src/sql_databases/tutorial002_an_py310.py:79
session.refresh(db_hero)
return db_hero
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(
session: SessionDep,
offset: int = 0,
limit: Annotated[int, Query(le=100)] = 100,
):
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int, session: SessionDep):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(hero_id: int, hero: HeroUpdate, session: SessionDep):
hero_db = session.get(Hero, hero_id)
if not hero_db:
raise HTTPException(status_code=404, detail="Hero not found")
hero_data = hero.model_dump(exclude_unset=True)
hero_db.sqlmodel_update(hero_data)
session.add(hero_db)
session.commit()
session.refresh(hero_db)
return hero_db
@app.delete("/heroes/{hero_id}")
def delete_hero(hero_id: int, session: SessionDep):View on GitHub (pinned to 3e8d1526d8)
Solutions
- Validate the ID exists before the request.
- Point the app at the correct seeded SQLite database file.
- Have the client treat a 404 as a normal not-found.
- Return None / 204 from the endpoint if absence is not exceptional.
Example fix
// before
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int, session: SessionDep):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero
// after
@app.get("/heroes/{hero_id}", response_model=HeroPublic | None)
def read_hero(hero_id: int, session: SessionDep):
return session.get(Hero, hero_id) Defensive patterns
Strategy: try-catch
Try / catch
import httpx
try:
r = httpx.get(f"http://localhost:8000/heroes/{hero_id}")
r.raise_for_status()
except httpx.HTTPStatusError as e:
if e.response.status_code == 404:
hero = None
else:
raise Prevention
- Validate the ID against the known list before requesting it.
- Keep database.db persistent and seeded.
- Return Optional response_model if a missing row is not exceptional.
- Handle 404 explicitly on the client to avoid crashes.
When it happens
Trigger: GET /heroes/{hero_id} against an ID that has no Hero row (deleted, never created, or wrong DB file).
Common situations: Client requests a hero ID obtained from a stale list or external source; database.db reset; the row was deleted after being listed.
Related errors
AI-assisted analysis of tiangolo/fastapi@3e8d1526d8 (2026-08-11).
Data as JSON: /api/errors/5f46f771e7b8e454.
Report an issue: GitHub.