tiangolo/fastapi · error · HTTPException
Item already exists
Error message
Item already exists
What it means
Raised (status 409 Conflict) by POST /items/ when the incoming Item's id already exists as a key in fake_db. The check runs only after the token passes; it enforces id uniqueness over the in-memory store. Because fake_db starts pre-seeded with 'foo' and 'bar', POSTing either of those ids always conflicts.
Solutions
- Generate a unique id per request (e.g. uuid4) instead of reusing 'foo'/'bar'.
- On 409, treat it as success if the stored item equals the one you tried to create (idempotent replay).
- Use PUT /items/{id} with upsert semantics if creation-on-conflict is desired.
Example fix
// before
POST /items/ {"id": "foo", "title": "Foo"}
// after
POST /items/ {"id": "<uuid4>", "title": "Foo"} Defensive patterns
Strategy: try-catch
Validate before calling
import uuid, httpx
def create_unique(item: dict):
item = {**item, 'id': str(uuid.uuid4())}
return httpx.post('http://localhost:8000/items/', json=item, headers={'X-Token': 'coneofsilence'}) Type guard
def is_unique_id(proposed: str, existing: set[str]) -> bool:
return proposed not in existing Try / catch
try:
resp = httpx.post(url, json=item, headers=headers)
resp.raise_for_status()
except httpx.HTTPStatusError as e:
if e.response.status_code == 409:
# idempotent: if stored item equals ours, treat as success
... Prevention
- Generate ids with uuid4 rather than fixed literals.
- Make POST idempotent by design (idempotency-key header).
- On 409, fetch the existing item and compare before erroring.
When it happens
Trigger: POST /items/ with a body whose 'id' is 'foo' or 'bar' (pre-seeded), or POSTing the same id twice in one server process. The second request for any id triggers 409.
Common situations: Idempotency-replay scenarios: a client retries a POST after a timeout, the first succeeded, so the retry 409s. Or the client uses a fixed/guessed id like 'foo' that is already seeded.
Related errors
AI-assisted analysis of tiangolo/fastapi@3e8d1526d8 (2026-08-11).
Data as JSON: /api/errors/4f570283ed13aa64.
Report an issue: GitHub.
Appendix: source
Thrown at docs_src/app_testing/app_b_an_py310/main.py:36
title: str
description: str | None = None
@app.get("/items/{item_id}", response_model=Item)
async def read_main(item_id: str, x_token: Annotated[str, Header()]):
if x_token != fake_secret_token:
raise HTTPException(status_code=400, detail="Invalid X-Token header")
if item_id not in fake_db:
raise HTTPException(status_code=404, detail="Item not found")
return fake_db[item_id]
@app.post("/items/")
async def create_item(item: Item, x_token: Annotated[str, Header()]) -> Item:
if x_token != fake_secret_token:
raise HTTPException(status_code=400, detail="Invalid X-Token header")
if item.id in fake_db:
raise HTTPException(status_code=409, detail="Item already exists")
fake_db[item.id] = item.model_dump()
return item
View on GitHub (pinned to 3e8d1526d8)