can1357/oh-my-pi · warning · HTTPException
invalid signature
Error message
invalid signature
What it means
POST /webhook/github verifies the X-Hub-Signature-256 HMAC-SHA256 header against GITHUB_WEBHOOK_SECRET using github_events.verify_signature; a mismatch raises HTTPException 401 'invalid signature' so forged or misconfigured deliveries never enter the queue.
Source
Thrown at python/robomp/src/server.py:342
raise HTTPException(503, "not initialized")
return {"status": "ready"}
@app.post("/webhook/github")
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:View on GitHub (pinned to 9690622007)
Solutions
- Confirm GITHUB_WEBHOOK_SECRET in robomp exactly matches the webhook secret configured in GitHub settings (no quotes/whitespace).
- After changing the secret, restart the orchestrator so Settings reload.
- Ensure any reverse proxy forwards the raw body unmodified and passes through X-Hub-Signature-256.
- Verify with a redelivery from the GitHub webhook 'Recent Deliveries' pane after fixing.
Example fix
// before (.env) GITHUB_WEBHOOK_SECRET="my secret " # stray quotes/trailing space // after (.env) GITHUB_WEBHOOK_SECRET=my-secret
Defensive patterns
Strategy: validation
Validate before calling
import hmac, hashlib
sig = 'sha256=' + hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(sig, headers['X-Hub-Signature-256']):
raise ValueError('local signature check failed — secret or body mismatch') Try / catch
try:
resp = httpx.post(url, content=body, headers=headers)
except httpx.HTTPStatusError as e:
if e.response.status_code == 401:
... # re-check GITHUB_WEBHOOK_SECRET on both sides before retrying Prevention
- Keep GITHUB_WEBHOOK_SECRET byte-identical in GitHub webhook settings and robomp .env (no quotes/whitespace)
- Rotate the secret in both places simultaneously and restart
- Ensure proxies forward the raw body and signature headers untouched
When it happens
Trigger: Webhook delivery whose HMAC does not match the configured secret — wrong/rotated GITHUB_WEBHOOK_SECRET on the robomp side, secret not updated in the GitHub repo/app webhook settings, a proxy re-signing or mangling the raw body, or replaying a captured request.
Common situations: Secret updated in GitHub but not in robomp's .env (or vice versa); trailing whitespace/quotes in the secret value; an intermediary (nginx, ngrok, tunnel) altering the payload before hashing; pointing two environments at one webhook.
Related errors
- Discord webhook credential must use HTTPS
- HTTP ${response.status}: server redirected a ${init.method}
- invalid json: {exc}
- V2 remote compaction failed (${response.status} ${response.s
- No response body for V2 compaction streaming
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/1626f07ae76cf534.
Report an issue: GitHub.