{"record":{"id":"21c70ba827f998e3","repo":"bytedance/deer-flow","slug":"invalid-json-body","errorCode":null,"errorMessage":"Invalid JSON body","messagePattern":"Invalid JSON body","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"backend/app/gateway/routers/github_webhooks.py","lineNumber":267,"sourceCode":"                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),\n        )\n        handled = True\n        # Publish inbound messages onto the channel bus so the\n        # ChannelManager picks them up and routes them to the right\n        # custom agents. No direct agent-run calls here.\n        from app.channels.service import get_channel_service\n\n        service = get_channel_service()\n        if service is None:\n            # Permanent state, not a transient failure: ``channels.github``\n            # is not enabled in this deployment. Returning 503 would mark\n            # this delivery \"failed\" and invite a manual \"Redeliver\" or an","sourceCodeStart":249,"sourceCodeEnd":285,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/backend/app/gateway/routers/github_webhooks.py#L249-L285","documentation":"Raised by the GitHub webhook receiver with status 400 when the (already signature-verified) request body fails json.loads — the payload is not valid JSON. Because verification happens first, a 400 here with a valid signature usually means GitHub sent an empty or non-JSON body, or the proxy corrupted the body after signature-relevant bytes (empty body maps to {}, so this specifically means non-empty, unparseable bytes).","triggerScenarios":"Sending form-encoded (application/x-www-form-urlencoded) payloads instead of JSON — GitHub does this when the webhook content-type is configured as form; proxies mangling the body (truncation, chunked-encoding bugs); handcrafted test requests with malformed JSON.","commonSituations":"GitHub webhook 'Content type' set to application/x-www-form-urlencoded in repo settings; gzip/deflate content-encoding not decoded by an intermediary; truncated bodies from MTU/proxy limits.","solutions":["In GitHub webhook/App settings, set Content type to application/json.","Verify the proxy forwards the body unmodified and handles content-encoding (identity or properly decoded).","For manual tests, validate the file first: python -m json.tool payload.json."],"exampleFix":"# before: GitHub webhook content type = application/x-www-form-urlencoded\n# payload arrives as 'payload=%7B...%7D' -> 400 Invalid JSON body\n\n# after: GitHub repo Settings -> Webhooks -> Edit -> Content type = application/json\n# then click 'Redeliver' on a recent delivery","handlingStrategy":"validation","validationCode":"const okPayload = (raw: string) => { try { JSON.parse(raw); return true; } catch { return false; } };\nif (!okPayload(body)) throw new Error('webhook body is not JSON — check GitHub content type');","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Set GitHub webhook Content type to application/json","Validate payloads locally before replay scripts","Ensure proxies don't truncate or re-encode bodies"],"tags":["http-400","webhooks","json","content-type","github"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}