{"record":{"id":"d7911340056da762","repo":"bytedance/deer-flow","slug":"missing-x-github-event-header","errorCode":null,"errorMessage":"Missing X-GitHub-Event header","messagePattern":"Missing X-GitHub-Event header","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"backend/app/gateway/routers/github_webhooks.py","lineNumber":255,"sourceCode":"                detail=f\"Webhook signature verification not configured. Set {_SECRET_ENV_VAR} or {_ALLOW_UNVERIFIED_ENV_VAR}=1 for unverified dev mode.\",\n            )\n        logger.warning(\n            \"github_webhook: accepting UNVERIFIED delivery (event=%s delivery=%s). %s=1 is set — dev/loopback mode ONLY. Do not use in production.\",\n            x_github_event,\n            x_github_delivery,\n            _ALLOW_UNVERIFIED_ENV_VAR,\n        )\n    else:\n        if not _verify_signature(secret, body, x_hub_signature_256):\n            logger.warning(\n                \"github_webhook: signature verification FAILED (event=%s delivery=%s)\",\n                x_github_event,\n                x_github_delivery,\n            )\n            raise HTTPException(status_code=401, detail=\"Invalid or missing X-Hub-Signature-256\")\n\n    if not x_github_event:\n        raise HTTPException(status_code=400, detail=\"Missing X-GitHub-Event header\")\n\n    # Parse JSON payload after signature is verified (verify-then-parse).\n    try:\n        payload: dict[str, Any] = json.loads(body) if body else {}\n    except json.JSONDecodeError as exc:\n        logger.warning(\n            \"github_webhook: invalid JSON body (event=%s delivery=%s): %s\",\n            x_github_event,\n            x_github_delivery,\n            exc,\n        )\n        raise HTTPException(status_code=400, detail=\"Invalid JSON body\") from exc\n\n    if x_github_event in _KNOWN_EVENTS:\n        logger.info(\n            \"github_webhook delivery=%s | %s\",\n            x_github_delivery,\n            _summarise_event(x_github_event, payload),","sourceCodeStart":237,"sourceCodeEnd":273,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/backend/app/gateway/routers/github_webhooks.py#L237-L273","documentation":"Raised by the GitHub webhook receiver with status 400 when the X-GitHub-Event header is absent or empty. The receiver needs the event name to route the payload (issues, pull_request, etc.), and real GitHub deliveries always include this header — its absence almost always means a misconfigured proxy stripped it or the sender is not GitHub.","triggerScenarios":"Manual curl/Postman testing without the X-GitHub-Event header; nginx or an API gateway dropping custom X-GitHub-* headers; corporate proxies stripping unknown X- headers; load-balancer health checks POSTing to the webhook URL.","commonSituations":"Proxy config with a header allow-list that omits GitHub's custom headers; webhook URL reused as a generic POST target in monitoring; incorrect ingress annotation like 'proxy_set_header X-GitHub-Event \"\"'.","solutions":["Ensure the delivery path forwards all X-GitHub-* headers (nginx: verify proxy_set_header directives don't override them; in general avoid header allow-lists on this route).","When testing manually, include -H 'X-GitHub-Event: push' (and the signature header).","Point health checks at /health, not the webhook endpoint."],"exampleFix":"# before\ncurl -X POST https://host/api/webhooks/github --data-binary @payload.json\n# -> 400 Missing X-GitHub-Event header\n\n# after\ncurl -X POST https://host/api/webhooks/github \\\n  -H 'X-GitHub-Event: push' \\\n  -H \"X-Hub-Signature-256: sha256=$SIG\" \\\n  --data-binary @payload.json","handlingStrategy":"validation","validationCode":"# nginx: forward GitHub headers untouched\nlocation /api/webhooks/github {\n    proxy_pass http://gateway:8001;\n    # do NOT override X-GitHub-* headers; avoid allow-lists on this route\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Forward all X-GitHub-* headers through every proxy hop","Include -H 'X-GitHub-Event: <event>' in manual tests","Point health checks at /health, not the webhook URL"],"tags":["http-400","webhooks","headers","proxy","github"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}