{"id":"cebdd7177513624a","repo":"tiangolo/fastapi","slug":"x-token-header-invalid-cebdd7","errorCode":null,"errorMessage":"X-Token header invalid","messagePattern":"X-Token header invalid","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"docs_src/dependencies/tutorial012_an_py310.py","lineNumber":8,"sourceCode":"from typing import Annotated\n\nfrom fastapi import Depends, FastAPI, Header, HTTPException\n\n\nasync def verify_token(x_token: Annotated[str, Header()]):\n    if x_token != \"fake-super-secret-token\":\n        raise HTTPException(status_code=400, detail=\"X-Token header invalid\")\n\n\nasync def verify_key(x_key: Annotated[str, Header()]):\n    if x_key != \"fake-super-secret-key\":\n        raise HTTPException(status_code=400, detail=\"X-Key header invalid\")\n    return x_key\n\n\napp = FastAPI(dependencies=[Depends(verify_token), Depends(verify_key)])\n\n\n@app.get(\"/items/\")\nasync def read_items():\n    return [{\"item\": \"Portal Gun\"}, {\"item\": \"Plumbus\"}]\n\n\n@app.get(\"/users/\")\nasync def read_users():","sourceCodeStart":1,"sourceCodeEnd":26,"githubUrl":"https://github.com/tiangolo/fastapi/blob/42a41db11f6882807ac3c057b942178d53b97438/docs_src/dependencies/tutorial012_an_py310.py#L1-L26","documentation":"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.","triggerScenarios":"Any request (GET /items/ or GET /users/) missing `X-Token: fake-super-secret-token`, or sending a wrong value.","commonSituations":"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.","solutions":["Send `X-Token: fake-super-secret-token` on every request.","Load the expected token from an environment variable instead of hardcoding so rotation is possible.","Return 401 instead of 400 for auth failures to follow HTTP semantics.","Document required headers in the OpenAPI security scheme."],"exampleFix":"# before\nif x_token != \"fake-super-secret-token\":\n    raise HTTPException(status_code=400, detail=\"X-Token header invalid\")\n\n# after\nimport os\nif x_token != os.environ[\"EXPECTED_TOKEN\"]:\n    raise HTTPException(status_code=401, detail=\"Invalid X-Token\")","handlingStrategy":"validation","validationCode":"TOKEN = \"fake-super-secret-token\"\nheaders = {\"X-Token\": TOKEN}\nassert headers[\"X-Token\"], \"X-Token required\"\nclient.get(\"/items/\", headers=headers)","typeGuard":"def has_valid_token(headers: dict) -> bool:\n    return headers.get(\"X-Token\") == \"fake-super-secret-token\"","tryCatchPattern":"r = client.get(\"/items/\", headers=headers)\nif r.status_code == 400 and r.json().get(\"detail\") == \"X-Token header invalid\":\n    # refresh credentials; do not retry with the same header\n    ...","preventionTips":["Inject required headers from a single client helper.","Store tokens in config/env, not literals in code paths that ship.","Treat header-invalid 400 as auth failure (consider 401 in your own APIs)."],"tags":["fastapi","authentication","headers","global-dependency","http-400"],"analyzedSha":"42a41db11f6882807ac3c057b942178d53b97438","analyzedAt":"2026-08-04T19:23:32.007Z","schemaVersion":2}