{"id":"6aa684c6d42f364a","repo":"tiangolo/fastapi","slug":"not-authorized","errorCode":null,"errorMessage":"Not authorized","messagePattern":"Not authorized","errorType":"http","errorClass":"HTTPException","httpStatus":403,"severity":"error","filePath":"docs_src/dependencies/tutorial013_an_py310.py","lineNumber":27,"sourceCode":"\n\nclass User(SQLModel, table=True):\n    id: int | None = Field(default=None, primary_key=True)\n    name: str\n\n\napp = FastAPI()\n\n\ndef get_session():\n    with Session(engine) as session:\n        yield session\n\n\ndef get_user(user_id: int, session: Annotated[Session, Depends(get_session)]):\n    user = session.get(User, user_id)\n    if not user:\n        raise HTTPException(status_code=403, detail=\"Not authorized\")\n\n\ndef generate_stream(query: str):\n    for ch in query:\n        yield ch\n        time.sleep(0.1)\n\n\n@app.get(\"/generate\", dependencies=[Depends(get_user)])\ndef generate(query: str):\n    return StreamingResponse(content=generate_stream(query))\n","sourceCodeStart":9,"sourceCodeEnd":39,"githubUrl":"https://github.com/tiangolo/fastapi/blob/42a41db11f6882807ac3c057b942178d53b97438/docs_src/dependencies/tutorial013_an_py310.py#L9-L39","documentation":"FastAPI returns HTTP 403 \"Not authorized\" when `session.get(User, user_id)` returns no row for the given id. The handler deliberately uses 403 rather than 404 to avoid leaking which user ids exist. The dependency is attached to the /generate route via `dependencies=[Depends(get_user)]`, so it runs before the StreamingResponse begins.","triggerScenarios":"GET /generate?query=...&user_id=<nonexistent> (or where the user_id query param does not map to a persisted User row).","commonSituations":"Clients passing an unauthenticated/typo'd user_id; the DB has no matching row; the SQLModel table not created/migrated; wrong DB connection string pointing at an empty database.","solutions":["Supply a user_id that exists in the User table (create it first if needed).","Run SQLModel metadata creation / migrations so the table is populated.","Verify the engine URL points at the intended database.","If leaking existence is acceptable for your threat model, switch to 404 for clarity."],"exampleFix":"# before\nuser = session.get(User, user_id)\nif not user:\n    raise HTTPException(status_code=403, detail=\"Not authorized\")\n\n# after (optional, if 404 semantics preferred)\nuser = session.get(User, user_id)\nif not user:\n    raise HTTPException(status_code=404, detail=\"User not found\")","handlingStrategy":"validation","validationCode":"# Verify user existence before calling /generate\nexists = users_table.get(user_id)\nif not exists:\n    raise PermissionError(\"Not authorized\")\nclient.get(\"/generate\", params={\"user_id\": user_id, \"query\": q})","typeGuard":"def user_exists(session, user_id: int) -> bool:\n    return session.get(User, user_id) is not None","tryCatchPattern":"r = client.get(\"/generate\", params={...})\nif r.status_code == 403:\n    # treat as unknown/unauthorized user; do not leak existence\n    ...","preventionTips":["Seed/confirm the user row before relying on the endpoint.","Ensure DB migrations created the User table.","Keep 403 vs 404 decision deliberate to avoid user enumeration."],"tags":["fastapi","authorization","sqlmodel","http-403","dependencies"],"analyzedSha":"42a41db11f6882807ac3c057b942178d53b97438","analyzedAt":"2026-08-04T19:23:32.007Z","schemaVersion":2}