{"record":{"id":"4f570283ed13aa64","repo":"tiangolo/fastapi","slug":"item-already-exists","errorCode":null,"errorMessage":"Item already exists","messagePattern":"Item already exists","errorType":"http","errorClass":"HTTPException","httpStatus":409,"severity":"error","filePath":"docs_src/app_testing/app_b_an_py310/main.py","lineNumber":36,"sourceCode":"    title: str\n    description: str | None = None\n\n\n@app.get(\"/items/{item_id}\", response_model=Item)\nasync def read_main(item_id: str, x_token: Annotated[str, Header()]):\n    if x_token != fake_secret_token:\n        raise HTTPException(status_code=400, detail=\"Invalid X-Token header\")\n    if item_id not in fake_db:\n        raise HTTPException(status_code=404, detail=\"Item not found\")\n    return fake_db[item_id]\n\n\n@app.post(\"/items/\")\nasync def create_item(item: Item, x_token: Annotated[str, Header()]) -> Item:\n    if x_token != fake_secret_token:\n        raise HTTPException(status_code=400, detail=\"Invalid X-Token header\")\n    if item.id in fake_db:\n        raise HTTPException(status_code=409, detail=\"Item already exists\")\n    fake_db[item.id] = item.model_dump()\n    return item\n","sourceCodeStart":18,"sourceCodeEnd":39,"githubUrl":"https://github.com/tiangolo/fastapi/blob/3e8d1526d83a90aaf7d6eb6dc682bf150f180b25/docs_src/app_testing/app_b_an_py310/main.py#L18-L39","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nPOST /items/  {\"id\": \"foo\", \"title\": \"Foo\"}\n// after\nPOST /items/  {\"id\": \"<uuid4>\", \"title\": \"Foo\"}","handlingStrategy":"try-catch","validationCode":"import uuid, httpx\ndef create_unique(item: dict):\n    item = {**item, 'id': str(uuid.uuid4())}\n    return httpx.post('http://localhost:8000/items/', json=item, headers={'X-Token': 'coneofsilence'})","typeGuard":"def is_unique_id(proposed: str, existing: set[str]) -> bool:\n    return proposed not in existing","tryCatchPattern":"try:\n    resp = httpx.post(url, json=item, headers=headers)\n    resp.raise_for_status()\nexcept httpx.HTTPStatusError as e:\n    if e.response.status_code == 409:\n        # idempotent: if stored item equals ours, treat as success\n        ...","preventionTips":["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."],"tags":["fastapi","conflict","duplicate","httpexception","app-testing"],"backgroundTag":null,"analyzedSha":"3e8d1526d83a90aaf7d6eb6dc682bf150f180b25","analyzedAt":"2026-08-11T02:34:52.986Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}