#6808·keep

Sentry provider crash-loops the backend at startup when Sentry returns an error with an empty body

Author: joborduCreated Sep 10, 2026Updated Sep 10, 2026

Summary

Provisioning a sentry provider via KEEP_PROVIDERS against a current Sentry SaaS org crash-loops keep-backend, taking the whole deployment down rather than reporting a failed scope.

Root cause

SentryProvider.validate_scopes() assumes every non-ok response carries a JSON body and calls response.json() on five error paths. Sentry does not guarantee that — a 404 from an endpoint that no longer exists returns an empty body — so response.json() raises:

JSONDecodeError: Expecting value: line 1 column 1 (char 0)

ProvidersService.provision_providers() calls validate_scopes() at application startup and does not catch the exception, so this is fatal rather than contained.

Why it triggers on a normal setup

The project:write check POSTs to the legacy per-project endpoint:

POST /projects/{org}/{project}/plugins/webhooks/

That endpoint is gone from modern Sentry SaaS, which answers 404 with an empty body — hitting the unguarded .json() directly.

Reproduce

  1. Create a Sentry org-level internal integration token.
  2. Provision a sentry provider through KEEP_PROVIDERS, e.g. {"my-sentry":{"type":"sentry","authentication":{"api_key":"…","organization_slug":"…"}}}
  3. Start the backend.

keep-backend crash-loops on the trace above.

Ruled out by experiment

Attempt Result
"install_webhook": false no effect — validate_scopes runs during install regardless
Removing project:write from the token no effect — the POST still 404s with an empty body
Upgrading no effect — the same POST is on main today

Notable asymmetry

Installing the same provider via POST /providers/install returns a clean HTTP 412 and the backend survives. Only the startup provisioning path is fatal, which is what made this surprising to hit.

Versions

Observed on 0.54.2; the same code is on main.

Fix

PR #6807 makes the error-detail extraction tolerant of a missing/non-dict/unparseable body across all five paths, so a failed scope is reported rather than raised.

Note project:write is mandatory=False (mandatory_for_webhook=True), so with the crash fixed, install succeeds and only webhook installation is affected. Whether project:write should still probe the removed plugins/webhooks endpoint is a separate question — happy to address it if you'd like it in the same PR.