Skip to content

fix: guard lookupField against Null body in ensureBackendReachable#5310

Open
blackheaven wants to merge 2 commits into
developfrom
gdifolco/fix-flaky-tests-lookup
Open

fix: guard lookupField against Null body in ensureBackendReachable#5310
blackheaven wants to merge 2 commits into
developfrom
gdifolco/fix-flaky-tests-lookup

Conversation

@blackheaven

Copy link
Copy Markdown
Contributor

When the federator returns a response with an empty/Null JSON body during backend warmup, lookupField res.json "inner" threw an uncaught assertion failure that aborted the retry loop (the surrounding E.try only catches HttpException). Wrap the call with catchAll so a Null body yields Nothing and the retry continues, instead of crashing testNotificationsForOfflineBackends.

Checklist

  • Add a new entry in an appropriate subdirectory of changelog.d
  • Read and follow the PR guidelines

When the federator returns a response with an empty/Null JSON body
during backend warmup, lookupField res.json "inner" threw an
uncaught assertion failure that aborted the retry loop (the
surrounding E.try only catches HttpException). Wrap the call with
catchAll so a Null body yields Nothing and the retry continues,
instead of crashing testNotificationsForOfflineBackends.
@blackheaven blackheaven marked this pull request as ready for review July 2, 2026 21:02
@blackheaven blackheaven requested a review from a team as a code owner July 2, 2026 21:02
@zebot zebot added the ok-to-test Approved for running tests in CI, overrides not-ok-to-test if both labels exist label Jul 2, 2026
Comment thread integration/test/Testlib/ModService.hs Outdated
-- but ingress is working, since we're processing the request.
let is200 = res.status == 200
mInner <- lookupField res.json "inner"
mInner <- lookupField res.json "inner" `catchAll` const (pure Nothing)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

…rors

The previous approach wrapped lookupField with catchAll to swallow the
AssertionFailure thrown when the federator returns a non-object body
during backend warmup (empty, JSON null, or Prometheus metrics text).

That had two problems:
1. catchAll masks all exceptions, not just the AssertionFailure we
   care about.
2. The status check (res.status == 200) alone can false-positive when
   the metrics middleware returns HTTP 200, declaring the backend
   reachable before brig is actually serving api-version.

Replace it with a body-shape guard: only consider the backend reachable
when res.json is a proper JSON Object. For 200 responses, additionally
require the VersionInfo "supported" field. The retry loop now genuinely
waits for warmup to complete.

See review feedback on #5310.
@blackheaven

Copy link
Copy Markdown
Contributor Author

Good catch — you're right that it's not just a Null body. I looked into where the metrics text comes from: the federator (and every wire-server service) serves Prometheus metrics at /i/metrics via wai-middleware-prometheus (configured in Data.Metrics.Servant). During dynamic-backend warmup the /rpc/<domain>/brig/api-version probe can transiently receive a non-JSON body — metrics text, empty, or JSON null — before brig is actually ready.

Rather than catching the AssertionFailure (which is what the first revision did), I have reworked the check to guard on the response body shape instead. The retry loop now only succeeds when res.json is a proper JSON Object:

  • 200 → require the VersionInfo supported field to be present (so a stray 200 with a metrics body cannot false-positive and declare the backend reachable prematurely)
  • 533 → check inner.label == "federation-denied" (unchanged behaviour)
  • anything else (Null, empty, metrics text, non-object JSON) → pure False, keep retrying

This way ensureBackendReachable genuinely waits for warmup to complete instead of masking transient malformed responses. No exception catching needed — lookupField on a confirmed Object for a single key cannot throw.

The deeper question of why the federator occasionally routes the probe to a metrics-shaped response during warmup is still worth investigating separately, but this fix makes the poll robust regardless of the exact transient shape.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ok-to-test Approved for running tests in CI, overrides not-ok-to-test if both labels exist

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants