Significant-Gravitas/AutoGPT · warning · HTTPException

Invalid payload

Error message

Invalid payload

What it means

Raised (400) by /credits/stripe_webhook when stripe.Webhook.construct_event raises ValueError, which the Stripe SDK uses for a malformed request body (payload is not valid JSON / cannot be parsed as an event). It fires before signature verification semantics matter — the body itself is unparseable.

Source

Thrown at autogpt_platform/backend/backend/api/features/v1.py:1437

    if not webhook_secret:
        # Guard: an empty secret allows HMAC forgery (attacker can compute a valid
        # signature over the same empty key). Reject all webhook calls when unconfigured.
        logger.error(
            "stripe_webhook: STRIPE_WEBHOOK_SECRET is not configured — "
            "rejecting request to prevent signature bypass"
        )
        raise HTTPException(status_code=503, detail="Webhook not configured")

    # Get the raw request body
    payload = await request.body()
    # Get the signature header
    sig_header = request.headers.get("stripe-signature")

    try:
        event = stripe.Webhook.construct_event(payload, sig_header, webhook_secret)
    except ValueError:
        # Invalid payload
        raise HTTPException(status_code=400, detail="Invalid payload")
    except stripe.SignatureVerificationError:
        # Invalid signature
        raise HTTPException(status_code=400, detail="Invalid signature")

    # Defensive payload extraction. A malformed payload (missing/non-dict
    # `data.object`, missing `id`) would otherwise raise KeyError/TypeError
    # AFTER signature verification — which Stripe interprets as a delivery
    # failure and retries forever, while spamming Sentry with no useful info.
    # Acknowledge with 200 and a warning so Stripe stops retrying.
    event_id = event.get("id", "")
    event_type = event.get("type", "")

    # Event-level dedup: short-circuit identical re-deliveries before any
    # handler runs. Stripe retries the same event.id on non-2xx responses, and
    # not every downstream handler is independently idempotent.
    if not await _claim_stripe_event(event_id):
        logger.info(
            "stripe_webhook: event %s (%s) already processed; skipping",

View on GitHub (pinned to 9c8bb5550f)

Solutions

  1. Confirm the caller is really Stripe: webhooks must be raw POSTs of Stripe event JSON with a stripe-signature header.
  2. Remove any middleware that buffers/re-encodes the request body; the handler needs the untouched raw bytes.
  3. Reproduce with `stripe trigger <event>` or the Stripe CLI to verify genuine deliveries pass.
Defensive patterns

Strategy: validation

Validate before calling

if (request.headers.get('content-type') !== 'application/json') reject(400); // only at your own edge; genuine Stripe deliveries are raw JSON POSTs

Try / catch

Stripe retries 400s for up to ~3 days; a persistent 400 pattern means a proxy or non-Stripe caller is hitting the endpoint — fix the source, don't catch.

Prevention

When it happens

Trigger: Any non-Stripe client POSTing garbage (HTML error page, form-encoded data, truncated JSON) to /credits/stripe_webhook; a proxy or middleware that consumes and re-encodes the raw body incorrectly; curl tests without a proper JSON event body.

Common situations: Load balancer health probes hitting the webhook path; security scanners posting junk; middleware that reads request.body() before the handler and replaces it with a re-serialized object, breaking the exact bytes Stripe signed.

Related errors


AI-assisted analysis of Significant-Gravitas/AutoGPT@9c8bb5550f (2026-08-14). Data as JSON: /api/errors/c168b88ac6a169ac. Report an issue: GitHub.