bytedance/deer-flow · error · HTTPException

Invalid or missing X-Hub-Signature-256

Error message

Invalid or missing X-Hub-Signature-256

What it means

Raised by the GitHub webhook receiver with status 401 when a secret IS configured but the X-Hub-Signature-256 header is missing or does not match HMAC-SHA256(secret, raw_body). The receiver verifies the signature over the exact raw request bytes before parsing JSON (verify-then-parse), so any body mutation by a proxy breaks it.

Source

Thrown at backend/app/gateway/routers/github_webhooks.py:252

            )
            raise HTTPException(
                status_code=503,
                detail=f"Webhook signature verification not configured. Set {_SECRET_ENV_VAR} or {_ALLOW_UNVERIFIED_ENV_VAR}=1 for unverified dev mode.",
            )
        logger.warning(
            "github_webhook: accepting UNVERIFIED delivery (event=%s delivery=%s). %s=1 is set — dev/loopback mode ONLY. Do not use in production.",
            x_github_event,
            x_github_delivery,
            _ALLOW_UNVERIFIED_ENV_VAR,
        )
    else:
        if not _verify_signature(secret, body, x_hub_signature_256):
            logger.warning(
                "github_webhook: signature verification FAILED (event=%s delivery=%s)",
                x_github_event,
                x_github_delivery,
            )
            raise HTTPException(status_code=401, detail="Invalid or missing X-Hub-Signature-256")

    if not x_github_event:
        raise HTTPException(status_code=400, detail="Missing X-GitHub-Event header")

    # Parse JSON payload after signature is verified (verify-then-parse).
    try:
        payload: dict[str, Any] = json.loads(body) if body else {}
    except json.JSONDecodeError as exc:
        logger.warning(
            "github_webhook: invalid JSON body (event=%s delivery=%s): %s",
            x_github_event,
            x_github_delivery,
            exc,
        )
        raise HTTPException(status_code=400, detail="Invalid JSON body") from exc

    if x_github_event in _KNOWN_EVENTS:
        logger.info(

View on GitHub (pinned to 1dd6ba1acb)

Solutions

  1. Re-sync the secret: set the identical value in the GitHub App/webhook settings and the Gateway env var, then redeliver from GitHub's webhook UI.
  2. Ensure the reverse proxy passes the request body through byte-for-byte untouched (no body filters/rewrites) and forwards the X-Hub-Signature-256 header.
  3. When testing manually, compute the header: sha256 hex of HMAC over the exact body bytes you send.

Example fix

# before (test client sends no/incorrect signature)
curl -X POST https://host/api/webhooks/github -H 'X-GitHub-Event: push' -d @payload.json
# -> 401 Invalid or missing X-Hub-Signature-256

# after
SIG=$(openssl dgst -sha256 -hmac "$GITHUB_WEBHOOK_SECRET" -hex < payload.json | awk '{print $2}')
curl -X POST https://host/api/webhooks/github \
  -H "X-GitHub-Event: push" \
  -H "X-Hub-Signature-256: sha256=$SIG" \
  --data-binary @payload.json
Defensive patterns

Strategy: validation

Validate before calling

# verify locally before sending: HMAC over exact body bytes
import hmac, hashlib
sig = 'sha256=' + hmac.new(secret.encode(), body_bytes, hashlib.sha256).hexdigest()
assert sig == sent_header, 'signature mismatch — check secret and body bytes'

Try / catch

try { await deliver(); } catch (e) { if (e.status === 401) { await resyncWebhookSecret(); await redeliver(); } throw e; }

Prevention

When it happens

Trigger: GitHub webhook secret in repo settings differs from the Gateway's configured secret; an intermediate proxy (nginx with body rewrite, request modification, or re-encoding) altering the raw body; replaying a captured request with a modified payload; missing signature header from a non-Github sender.

Common situations: Secret regenerated on one side only; nginx lua/scripts rewriting request bodies; line-ending or encoding transformations; load balancer appending trailers; using a test client that doesn't compute the HMAC.

Related errors


AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14). Data as JSON: /api/errors/b944a19572b4d657. Report an issue: GitHub.