can1357/oh-my-pi · info · HTTPException
invalid json: {exc}
Error message
invalid json: {exc} What it means
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.
Source
Thrown at python/robomp/src/server.py:346
async def webhook(
request: Request,
x_github_event: str = Header(..., alias="X-GitHub-Event"),
x_github_delivery: str = Header(..., alias="X-GitHub-Delivery"),
x_hub_signature_256: str | None = Header(None, alias="X-Hub-Signature-256"),
) -> JSONResponse:
bag = request.app.state.bag
cfg: Settings = bag["settings"]
body = await request.body()
if not github_events.verify_signature(
cfg.github_webhook_secret.get_secret_value(),
body,
x_hub_signature_256,
):
raise HTTPException(status.HTTP_401_UNAUTHORIZED, "invalid signature")
try:
payload = await request.json()
except Exception as exc:
raise HTTPException(status.HTTP_400_BAD_REQUEST, f"invalid json: {exc}") from exc
db: Database = bag["db"]
issue_cache: _IssueBrowseCache = bag["issue_browse_cache"]
await issue_cache.apply_webhook(
event_type=x_github_event,
payload=payload,
allowlist=cfg.repo_allowlist,
)
# Keep the local search index fresh from every delivery that carries an
# issue/PR object — including ones the router will skip.
if x_github_event in ("issues", "issue_comment") or x_github_event.startswith("pull_request"):
repo_full = str((payload.get("repository") or {}).get("full_name") or "")
if repo_full and repo_full in cfg.repo_allowlist:
try:
issue_index.ingest_webhook_payload(db, repo_full, x_github_event, payload)
except Exception:
log.exception("issue index webhook ingest failed", extra={"repo": repo_full})
View on GitHub (pinned to 9690622007)
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.
Example fix
// before curl -X POST http://host:8080/webhook/github -d 'not json' // after curl -X POST http://host:8080/webhook/github \ -H 'Content-Type: application/json' \ --data-binary @payload.json
Defensive patterns
Strategy: validation
Validate before calling
import json payload = json.loads(body) # validate before sending assert isinstance(payload, dict) and 'action' in payload
Try / catch
try:
resp = httpx.post(url, json=payload, headers=headers)
except httpx.HTTPStatusError as e:
if e.response.status_code == 400:
... # read 'invalid json: ...' detail and fix the payload Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- bridge call {name!r}: non-JSON response: {body[:200]!r}
- Jina API returned invalid response: expected an object or ar
- invalid signature
- {str(exc)} (ManualTriageError)
- Replacement text is not valid UTF-8: {err}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/0aa321892dcba4cd.
Report an issue: GitHub.