Sentry: tolerate GlitchTip's empty 200 body

dcda945 · gmorell · 2026-06-26 22:07

1 files +19 -9
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 JSONit 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