tiangolo/fastapi · error · HTTPException
Invalid X-Token header
Error message
Invalid X-Token header
What it means
Same logic as error 0 but in the non-Annotated (legacy default-parameter) variant of the file: GET /items/{item_id} raises 400 when the X-Token header != 'coneofsilence'. Functionally identical; only the header-injection style differs (x_token: str = Header() vs Annotated[str, Header()]).
Solutions
- Send X-Token: coneofsilence.
- Keep a single source of truth for the token across all variants of the app.
- Prefer the Annotated form (the py310 file is the older style) to stay consistent with current FastAPI docs.
Example fix
// before GET /items/foo X-Token: anything // after GET /items/foo X-Token: coneofsilence
Defensive patterns
Strategy: validation
Validate before calling
import httpx
resp = httpx.get('http://localhost:8000/items/foo', headers={'X-Token': 'coneofsilence'}) Type guard
def is_valid_x_token(value: object) -> bool:
return isinstance(value, str) and value == 'coneofsilence' Prevention
- Keep a single token constant shared across file variants.
- Prefer the Annotated variant going forward.
- Add a client middleware that injects the header automatically.
When it happens
Trigger: GET /items/{item_id} with an X-Token header that is not exactly 'coneofsilence'.
Common situations: Client was written against the Annotated variant and reused on this variant; token drift; header stripped by a proxy.
Related errors
AI-assisted analysis of tiangolo/fastapi@3e8d1526d8 (2026-08-11).
Data as JSON: /api/errors/dc6385ebb4e23fbc.
Report an issue: GitHub.
Appendix: source
Thrown at docs_src/app_testing/app_b_py310/main.py:23
fake_db = {
"foo": {"id": "foo", "title": "Foo", "description": "There goes my hero"},
"bar": {"id": "bar", "title": "Bar", "description": "The bartenders"},
}
app = FastAPI()
class Item(BaseModel):
id: str
title: str
description: str | None = None
@app.get("/items/{item_id}", response_model=Item)
async def read_main(item_id: str, x_token: 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: 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)