Significant-Gravitas/AutoGPT · warning · HTTPException

Invalid signature

Error message

Invalid signature

What it means

Raised (400) by /credits/stripe_webhook when stripe.Webhook.construct_event raises SignatureVerificationError: the payload parsed fine, but the stripe-signature header does not contain a valid HMAC-SHA256 signature for the body under the configured webhook secret. This is how Stripe's SDK reports forged or mis-keyed deliveries.

Source

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

        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",
            event_id,
            event_type,
        )

View on GitHub (pinned to 9c8bb5550f)

Solutions

  1. Verify STRIPE_WEBHOOK_SECRET matches the signing secret of the exact Stripe webhook endpoint delivering the event (test and live secrets differ).
  2. Ensure nothing between Stripe and the app mutates the body — disable body-modifying transforms on the ingress.
  3. If testing locally, send events via `stripe listen --forward-to ...` and use the CLI's printed secret.
Defensive patterns

Strategy: validation

Validate before calling

# confirm the secret matches the endpoint before going live
import stripe, os
ep = stripe.WebhookEndpoint.list(limit=100)
assert any(os.environ['STRIPE_WEBHOOK_SECRET'] for _ in [0])  # placeholder; real check: secret comes from the same endpoint config

Try / catch

Persistent SignatureVerificationError on genuine Stripe deliveries = secret mismatch or body mutation; investigate ingress (compression/rewrite) and secret config — do not catch and ignore.

Prevention

When it happens

Trigger: A request with parseable JSON but a missing/incorrect stripe-signature header: attackers replaying modified events, a legitimate Stripe endpoint whose signing secret differs from STRIPE_WEBHOOK_SECRET on this server (e.g. test-mode events hitting a prod secret), or body bytes altered in transit by a proxy.

Common situations: Multiple webhook endpoints in Stripe sharing one URL but different secrets; secret rotated in Stripe but not in the app; TLS-terminating proxy that rewrites the body (e.g. compression/encoding changes); local dev pointing the Stripe CLI at an env with another env's secret.

Related errors


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