fix/subscription error states
Summary
Two unrelated bugs in the subscription page, both about a UI state that gets stuck.
The post-checkout screen. When you come back from Stripe checkout the page
polls for your new subscription, and after a few seconds gives up and says
"GlitchTip was unable to find a subscription for your account." Nothing ever
takes that message back. It is a stored flag whose only reset is leaving the
page, and the template shows it with an or, so it sits on top of a
subscription that has since loaded. Someone who just paid us is told their
subscription does not exist.
The subscription often does arrive after we give up. The last poll starts one more request before it stops the timer, switching org refetches, and turning overage on refetches too. So clearing the flag from the poll would have missed all three. Instead the message is now worked out from the flag plus whether we actually have a subscription, so it goes away as soon as one exists.
Errors on the overage toggle. Three things, all on one line:
- If the request failed at the network level the spinner ran forever and the
toggle stayed disabled, because the error skipped every place we reset the
loading flag. It now resets in a
finally. - We accepted an empty error list from the API and then read the first item of it. About 20 places do that. Rejecting it in the shared check means they all fall back to a normal message instead.
- We had no case for 402 or 502, so a declined card showed "Something went wrong" instead of the real reason. Both now pass the server's message through.
The error helpers move to api-errors.ts so they can be tested. api.ts sets
up its client on import and touches document, so it cannot be loaded in a
test. It re-exports, so nothing else changes.
This is based on !783 because it touches the same function. It will retarget to master once that merges.
Testing
Browser tested against a local backend, both bugs before and after:
- Post-checkout, with the subscription arriving late. Before: message appears at 4.5s and stays there over the loaded subscription. After: never appears, page renders normally.
- Overage toggle with the request blocked at the network level. Before: spinner never stops, toggle stuck. After: spinner stops, toggle usable, message shown.
Unit tests for both, in the same style as the existing specs. Putting either bug back makes them fail. Full suite green (60), build and lint clean.
Worth noting: browser testing caught a bug in my first attempt at the
post-checkout fix that the unit tests did not. The subscription endpoint returns
200 with null when there is no subscription yet, which counts as the request
having succeeded, so the message never showed at all.
Not in this MR
- FE-01 (the spend cap max is 100x the backend's) needs more than lowering the number. Orgs with a saved cap above the new limit would get a toggle that silently refuses with no error shown, and the backend limit is configurable anyway, so it should come from the settings API.
- FE-08 is not a bug. The backend already falls back to the current period dates, and "fixing" it would break the forecast for annual plans.
- Stripe waits up to 10s for the webhook before redirecting, so our 4-6s poll can give up before their own window closes. Their guidance is that a timeout should read as "still processing", not an error. Worth a follow-up.
AI disclosure: Claude Code. Both bugs reproduced and verified by hand in a browser, fixes and tests drafted with AI and reviewed by hand. Agent review of the plan found that two other bugs on my list were not real.
- I am respecting Sentry's proprietary Business Source License (BSL) by implementing code in a clean room environment. I have not read any BSL Sentry code. It is OK to read MIT licensed code.