Sentry: tolerate GlitchTip's empty 200 body
dcda945 · gmorell · 2026-06-26 22:07
Message
{commit_body(@commit)}
Files changed
modified
lib/git_gud/sentry_client.ex
+19
−9
@@ -1,14 +1,17 @@
| 1 | 1 | defmodule GitGud.SentryClient do |
| 2 | 2 | @moduledoc """ |
| 3 | − Sentry HTTP client backed by Req (already a dependency) instead of the | |
| 4 | − default hackney client. | |
| 3 | + Sentry HTTP client backed by Req (already a dependency). | |
| 5 | 4 | |
| 6 | − Against our self-hosted GlitchTip (s.gmp.io), hackney's | |
| 7 | − `:with_body` request returns an empty response body even though the | |
| 8 | − server replies `{"id": "..."}` (verified with curl over HTTP/1.1, /2, | |
| 9 | − and gzipped). The event still ingests, but the SDK can't read the | |
| 10 | − confirmation and reports a false `request_failure`, which also triggers | |
| 11 | − pointless retries. Req reads the body correctly. | |
| 5 | + Works around our self-hosted GlitchTip (s.gmp.io) returning an **empty | |
| 6 | + 200 body** on successful envelope ingestion. `Sentry.Transport` | |
| 7 | + requires a 200 response body to be valid JSON — it runs | |
| 8 | + `Sentry.JSON.decode/2` on it to read the event id (see | |
| 9 | + `deps/sentry/lib/sentry/transport.ex`) — so an empty body raises a | |
| 10 | + `JSON.DecodeError` and the SDK reports a false `request_failure` (and | |
| 11 | + retries), even though the event was accepted. This is not a client bug: | |
| 12 | + both hackney and Req faithfully return the empty body. We substitute | |
| 13 | + `"{}"` for an empty successful body so the decode yields `id: nil`, | |
| 14 | + which Sentry treats as success. | |
| 12 | 15 | """ |
| 13 | 16 | @behaviour Sentry.HTTPClient |
| 14 | 17 |
@@ -33,13 +36,20 @@ defmodule GitGud.SentryClient do
| 33 | 36 | retry: false |
| 34 | 37 | ) do |
| 35 | 38 | {:ok, %Req.Response{status: status, headers: resp_headers, body: resp_body}} -> |
| 36 | − {:ok, status, flatten_headers(resp_headers), resp_body} | |
| 39 | + {:ok, status, flatten_headers(resp_headers), normalize_body(status, resp_body)} | |
| 37 | 40 | |
| 38 | 41 | {:error, exception} -> |
| 39 | 42 | {:error, exception} |
| 40 | 43 | end |
| 41 | 44 | end |
| 42 | 45 | |
| 46 | + # GlitchTip returns an empty 200 body on a successful send; Sentry needs | |
| 47 | + # valid JSON to decode. Give it an empty object so the send registers as | |
| 48 | + # success (id: nil). Non-2xx bodies are left untouched for accurate | |
| 49 | + # error reporting. | |
| 50 | + defp normalize_body(status, "") when status in 200..299, do: "{}" | |
| 51 | + defp normalize_body(_status, body), do: body | |
| 52 | + | |
| 43 | 53 | # Sentry.HTTPClient wants headers as a list of {key, value}; Req returns |
| 44 | 54 | # a map of key => [values]. |
| 45 | 55 | defp flatten_headers(headers) do |
Parents: dda7dbe