The Cur8Api5XXAlarm (eu-west-2) fired at 05:18 UTC, set off by the Checkly synthetic monitor probing GET /commitments-v2. The probe caught it first, but any investor whose commitment list landed in the same shape would have hit the same 500 on their portfolio.
One 500 in CloudWatch (Cur8ApiLogGroup), captured to Sentry, with a clean stack:
TypeError: Cannot destructure property 'investments' of 'transactionResources' as it is undefined. at CommitmentCoreService.shouldShowCommitmentOnPortfolio at CommitmentCoreService.getInvestorCommitments at CommitmentControllerV2.getInvestorCommitments
getInvestorCommitments builds a map of transaction resources keyed by commitment id, then iterates the commitment list. shouldShowCommitmentOnPortfolio destructures that resources object with no guard — so any commitment present in the list but missing from the map throws.
Not a regression: the destructure was authored in Dec 2024, the unguarded call site in Feb 2025. It sat latent for months; the synthetic probe simply hit a triggering shape. A commitment can be missing from the map when the resources query returns fewer rows than requested (a race against deletion, an eager-load edge case, or an id-coercion mismatch between the queries).
Default to an empty transaction-resources object at the call site (and the sibling call sites). That is semantically identical to a commitment with no transactions, so the endpoint returns the correct result instead of throwing.
The 500 is eliminated; the endpoint now falls back to the correct "no transactions → don't show on portfolio" behaviour. A follow-up was logged to chase the underlying map inconsistency, so the surgical fix doesn't mask it forever.