{"record":{"id":"23ef81dae03fef0d","repo":"tiangolo/fastapi","slug":"invalid-x-token-header","errorCode":null,"errorMessage":"Invalid X-Token header","messagePattern":"Invalid X-Token header","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"docs_src/app_testing/app_b_an_py310/main.py","lineNumber":25,"sourceCode":"\nfake_db = {\n    \"foo\": {\"id\": \"foo\", \"title\": \"Foo\", \"description\": \"There goes my hero\"},\n    \"bar\": {\"id\": \"bar\", \"title\": \"Bar\", \"description\": \"The bartenders\"},\n}\n\napp = FastAPI()\n\n\nclass Item(BaseModel):\n    id: str\n    title: str\n    description: str | None = None\n\n\n@app.get(\"/items/{item_id}\", response_model=Item)\nasync def read_main(item_id: str, x_token: Annotated[str, Header()]):\n    if x_token != fake_secret_token:\n        raise HTTPException(status_code=400, detail=\"Invalid X-Token header\")\n    if item_id not in fake_db:\n        raise HTTPException(status_code=404, detail=\"Item not found\")\n    return fake_db[item_id]\n\n\n@app.post(\"/items/\")\nasync def create_item(item: Item, x_token: Annotated[str, Header()]) -> Item:\n    if x_token != fake_secret_token:\n        raise HTTPException(status_code=400, detail=\"Invalid X-Token header\")\n    if item.id in fake_db:\n        raise HTTPException(status_code=409, detail=\"Item already exists\")\n    fake_db[item.id] = item.model_dump()\n    return item\n","sourceCodeStart":7,"sourceCodeEnd":39,"githubUrl":"https://github.com/tiangolo/fastapi/blob/3e8d1526d83a90aaf7d6eb6dc682bf150f180b25/docs_src/app_testing/app_b_an_py310/main.py#L7-L39","documentation":"This HTTPException (status 400) is raised by the GET /items/{item_id} handler when the request's X-Token header does not equal the hardcoded secret 'coneofsilence'. It is a hand-rolled authentication gate: FastAPI injects the header via Annotated[str, Header()], and a plain equality check rejects anything that does not match exactly. The status code 400 (rather than 401/403) is a docs-example choice, not a security best practice. It fires before the item lookup, so no resource access occurs without a valid token.","triggerScenarios":"A GET request to /items/{item_id} with a missing, empty, misspelled, or wrong X-Token header. Examples: omitting the header entirely (FastAPI then returns 422 for missing required header before this line), sending X-Token: wrong, or sending X-Token: ConeOfSilence (case mismatch).","commonSituations":"Developers copy the tutorial token 'coneofsilence' into a test client and later rotate it in one place but not another; CI tests forget to set the header; a frontend proxy strips custom headers; or the header is sent with surrounding whitespace or different casing of the value.","solutions":["Send the header exactly: X-Token: coneofsilence on every GET /items/{item_id} request.","If the header is present but wrong, confirm there is no trailing whitespace or quotes added by the HTTP client.","Move the secret out of source into an environment variable and load it on both server and client sides from the same source.","For production, replace this check with a real auth dependency (OAuth2/FastAPI Security) and use 401/403 instead of 400."],"exampleFix":"// before\ncurl -H 'X-Token: secret' http://localhost:8000/items/foo\n// after\ncurl -H 'X-Token: coneofsilence' http://localhost:8000/items/foo","handlingStrategy":"validation","validationCode":"import httpx\nSECRET = 'coneofsilence'\ndef valid_token(token: str) -> bool:\n    return token == SECRET\n# before the call:\nheaders = {'X-Token': SECRET} if valid_token(SECRET) else {}\nresp = httpx.get('http://localhost:8000/items/foo', headers=headers)","typeGuard":"def is_valid_x_token(value: object) -> bool:\n    return isinstance(value, str) and value == 'coneofsilence'","tryCatchPattern":null,"preventionTips":["Load the expected token from one shared config source on both server and client.","Centralize header construction in a single client function so every call is consistent.","Add an integration test asserting the header is sent on every protected route."],"tags":["fastapi","authentication","header","httpexception","app-testing"],"backgroundTag":null,"analyzedSha":"3e8d1526d83a90aaf7d6eb6dc682bf150f180b25","analyzedAt":"2026-08-11T02:34:52.986Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}