{"record":{"id":"0aa321892dcba4cd","repo":"can1357/oh-my-pi","slug":"invalid-json-exc-0aa321","errorCode":null,"errorMessage":"invalid json: {exc}","messagePattern":"invalid json: (.+?)","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"info","filePath":"python/robomp/src/server.py","lineNumber":346,"sourceCode":"    async def webhook(\n        request: Request,\n        x_github_event: str = Header(..., alias=\"X-GitHub-Event\"),\n        x_github_delivery: str = Header(..., alias=\"X-GitHub-Delivery\"),\n        x_hub_signature_256: str | None = Header(None, alias=\"X-Hub-Signature-256\"),\n    ) -> JSONResponse:\n        bag = request.app.state.bag\n        cfg: Settings = bag[\"settings\"]\n        body = await request.body()\n        if not github_events.verify_signature(\n            cfg.github_webhook_secret.get_secret_value(),\n            body,\n            x_hub_signature_256,\n        ):\n            raise HTTPException(status.HTTP_401_UNAUTHORIZED, \"invalid signature\")\n        try:\n            payload = await request.json()\n        except Exception as exc:\n            raise HTTPException(status.HTTP_400_BAD_REQUEST, f\"invalid json: {exc}\") from exc\n\n        db: Database = bag[\"db\"]\n        issue_cache: _IssueBrowseCache = bag[\"issue_browse_cache\"]\n        await issue_cache.apply_webhook(\n            event_type=x_github_event,\n            payload=payload,\n            allowlist=cfg.repo_allowlist,\n        )\n        # Keep the local search index fresh from every delivery that carries an\n        # issue/PR object — including ones the router will skip.\n        if x_github_event in (\"issues\", \"issue_comment\") or x_github_event.startswith(\"pull_request\"):\n            repo_full = str((payload.get(\"repository\") or {}).get(\"full_name\") or \"\")\n            if repo_full and repo_full in cfg.repo_allowlist:\n                try:\n                    issue_index.ingest_webhook_payload(db, repo_full, x_github_event, payload)\n                except Exception:\n                    log.exception(\"issue index webhook ingest failed\", extra={\"repo\": repo_full})\n","sourceCodeStart":328,"sourceCodeEnd":364,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/python/robomp/src/server.py#L328-L364","documentation":"After signature verification, /webhook/github parses the request body as JSON; any parsing exception is converted to HTTP 400 with 'invalid json: <exc>', keeping malformed payloads out of the dispatcher while still returning 202-shaped semantics for valid ones.","triggerScenarios":"A request passing HMAC check whose body is not valid JSON — sender posting form-encoded data, truncated body, wrong Content-Type with binary payload, or a test curl with malformed JSON.","commonSituations":"Hand-testing the endpoint with curl and a typo'd payload; a proxy that decompresses/re-encodes incorrectly; sending GitHub's urlencoded ping form instead of JSON; empty body with valid signature computed over the empty string.","solutions":["Ensure the client sends Content-Type: application/json with a well-formed JSON body.","If testing manually, send the exact delivery JSON GitHub shows in 'Recent Deliveries'.","Check intermediary proxies for body truncation or compression issues.","Real GitHub webhooks are always JSON — if this recurs in production, inspect what is actually posting to the endpoint."],"exampleFix":"// before\ncurl -X POST http://host:8080/webhook/github -d 'not json'\n// after\ncurl -X POST http://host:8080/webhook/github \\\n  -H 'Content-Type: application/json' \\\n  --data-binary @payload.json","handlingStrategy":"validation","validationCode":"import json\npayload = json.loads(body)  # validate before sending\nassert isinstance(payload, dict) and 'action' in payload","typeGuard":null,"tryCatchPattern":"try:\n    resp = httpx.post(url, json=payload, headers=headers)\nexcept httpx.HTTPStatusError as e:\n    if e.response.status_code == 400:\n        ...  # read 'invalid json: ...' detail and fix the payload","preventionTips":["Always send Content-Type: application/json with --data-binary for raw bodies","Validate payloads locally with json.loads before posting","Use GitHub's 'Recent Deliveries' JSON when hand-replaying events"],"tags":["http","json","webhook","bad-request"],"backgroundTag":"invalid-json-payload","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}