{"record":{"id":"fc01ee80af6d9930","repo":"tiangolo/fastapi","slug":"owner-error-e-fc01ee","errorCode":null,"errorMessage":"Owner error: {e}","messagePattern":"Owner error: (.+?)","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"docs_src/dependencies/tutorial008b_py310.py","lineNumber":20,"sourceCode":"\napp = FastAPI()\n\n\ndata = {\n    \"plumbus\": {\"description\": \"Freshly pickled plumbus\", \"owner\": \"Morty\"},\n    \"portal-gun\": {\"description\": \"Gun to create portals\", \"owner\": \"Rick\"},\n}\n\n\nclass OwnerError(Exception):\n    pass\n\n\ndef get_username():\n    try:\n        yield \"Rick\"\n    except OwnerError as e:\n        raise HTTPException(status_code=400, detail=f\"Owner error: {e}\")\n\n\n@app.get(\"/items/{item_id}\")\ndef get_item(item_id: str, username: str = Depends(get_username)):\n    if item_id not in data:\n        raise HTTPException(status_code=404, detail=\"Item not found\")\n    item = data[item_id]\n    if item[\"owner\"] != username:\n        raise OwnerError(username)\n    return item\n","sourceCodeStart":2,"sourceCodeEnd":31,"githubUrl":"https://github.com/tiangolo/fastapi/blob/3e8d1526d83a90aaf7d6eb6dc682bf150f180b25/docs_src/dependencies/tutorial008b_py310.py#L2-L31","documentation":"This is an HTTPException (status 400) raised inside a yield-based FastAPI dependency. The dependency get_username yields 'Rick', and the path operation raises a custom OwnerError when the requested item's owner does not match the yielded username. FastAPI routes exceptions thrown after a yield back into the dependency's except block, which converts OwnerError into a 400 HTTPException. It exists to demonstrate converting a domain/ownership exception into an HTTP error at the dependency boundary.","triggerScenarios":"GET /items/portal-gun with no special handling: 'portal-gun' is in the data dict (owner 'Rick'), so ownership check passes. The trigger is requesting an item whose owner != 'Rick' — but in this sample data both items are owned by Morty/Rick, so the real trigger is any item_id present in `data` whose 'owner' value differs from the hard-coded yielded 'Rick'. Concretely, GET /items/plumbus returns 400 because owner is 'Morty' while yielded username is 'Rick'.","commonSituations":"Building authorization/ownership checks via dependencies and needing to translate a non-HTTP exception raised downstream into a clean 400 response. Developers hit this when the yielded identity (hard-coded here) does not match the data's stored owner, or when they forget that the yield-dependency except block is the only place to catch downstream OwnerError.","solutions":["Confirm which item you requested and compare its 'owner' field against the username the dependency yields (here 'Rick'); request an item owned by that user.","If the yielded username should be dynamic, replace the hard-coded yield 'Rick' with real user resolution (e.g. from a token/session) so ownership matches.","Move the ownership check into the path operation and raise HTTPException(400) directly instead of relying on the yield-dependency except bridge, to make the failure explicit."],"exampleFix":"// before\ndef get_username():\n    try:\n        yield \"Rick\"\n    except OwnerError as e:\n        raise HTTPException(status_code=400, detail=f\"Owner error: {e}\")\n// after (dynamic user + explicit HTTP error)\ndef get_username(token: str = Depends(oauth2_scheme)):\n    user = resolve_user(token)\n    if not user:\n        raise HTTPException(status_code=401, detail=\"Not authenticated\")\n    try:\n        yield user.username\n    except OwnerError as e:\n        raise HTTPException(status_code=400, detail=f\"Owner error: {e}\")","handlingStrategy":"try-catch","validationCode":"import requests\nitem = 'portal-gun'\n# verify ownership locally before calling\nknown_owner = {'plumbus': 'Morty', 'portal-gun': 'Rick'}\nif known_owner.get(item) != 'Rick':\n    print('will fail with Owner error')","typeGuard":"def is_owned_by(username: str, item_id: str, data: dict) -> bool:\n    return item_id in data and data[item_id].get('owner') == username","tryCatchPattern":"import requests\nresp = requests.get('http://localhost:8000/items/plumbus')\ntry:\n    resp.raise_for_status()\nexcept requests.HTTPError as e:\n    if resp.status_code == 400 and 'Owner error' in resp.text:\n        print('Ownership check failed:', resp.json()['detail'])\n    else:\n        raise","preventionTips":["Make the yielded identity dynamic so it matches stored owners.","Document which usernames own which items for test clients.","Convert OwnerError to HTTPException at the point of the check, not only in the dependency except block."],"tags":["fastapi","dependencies","authorization","http-400"],"backgroundTag":null,"analyzedSha":"3e8d1526d83a90aaf7d6eb6dc682bf150f180b25","analyzedAt":"2026-08-11T02:34:52.986Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}