tiangolo/fastapi · error · HTTPException

X-Token header invalid

Error message

X-Token header invalid

What it means

FastAPI returns HTTP 400 with detail "X-Token header invalid" when the `X-Token` request header does not equal the hard-coded expected value "fake-super-secret-token". The check lives in `verify_token`, registered as an application-wide dependency via `FastAPI(dependencies=[Depends(verify_token), Depends(verify_key)])`, so it runs before every route (/items/ and /users/). It demonstrates global dependency-based API-key guarding.

Source

Thrown at docs_src/dependencies/tutorial012_an_py310.py:8

from typing import Annotated

from fastapi import Depends, FastAPI, Header, HTTPException


async def verify_token(x_token: Annotated[str, Header()]):
    if x_token != "fake-super-secret-token":
        raise HTTPException(status_code=400, detail="X-Token header invalid")


async def verify_key(x_key: Annotated[str, Header()]):
    if x_key != "fake-super-secret-key":
        raise HTTPException(status_code=400, detail="X-Key header invalid")
    return x_key


app = FastAPI(dependencies=[Depends(verify_token), Depends(verify_key)])


@app.get("/items/")
async def read_items():
    return [{"item": "Portal Gun"}, {"item": "Plumbus"}]


@app.get("/users/")
async def read_users():

View on GitHub (pinned to 42a41db11f)

Solutions

  1. Send `X-Token: fake-super-secret-token` on every request.
  2. Load the expected token from an environment variable instead of hardcoding so rotation is possible.
  3. Return 401 instead of 400 for auth failures to follow HTTP semantics.
  4. Document required headers in the OpenAPI security scheme.

Example fix

# before
if x_token != "fake-super-secret-token":
    raise HTTPException(status_code=400, detail="X-Token header invalid")

# after
import os
if x_token != os.environ["EXPECTED_TOKEN"]:
    raise HTTPException(status_code=401, detail="Invalid X-Token")
Defensive patterns

Strategy: validation

Validate before calling

TOKEN = "fake-super-secret-token"
headers = {"X-Token": TOKEN}
assert headers["X-Token"], "X-Token required"
client.get("/items/", headers=headers)

Type guard

def has_valid_token(headers: dict) -> bool:
    return headers.get("X-Token") == "fake-super-secret-token"

Try / catch

r = client.get("/items/", headers=headers)
if r.status_code == 400 and r.json().get("detail") == "X-Token header invalid":
    # refresh credentials; do not retry with the same header
    ...

Prevention

When it happens

Trigger: Any request (GET /items/ or GET /users/) missing `X-Token: fake-super-secret-token`, or sending a wrong value.

Common situations: Clients forgetting the header; sending the header with different casing of the value; sharing the wrong token; rotating the secret without notifying clients; proxy/gateway stripping custom headers.

Related errors


AI-assisted analysis of tiangolo/fastapi@42a41db11f (2026-08-04). Data as JSON: /data/errors/cebdd7177513624a.json. Report an issue: GitHub.