fix(checkout): reload page on expired session via error discriminator - #14168
Closed
detail-app[bot] wants to merge 1 commit into
Closed
detail-app[bot] wants to merge 1 commit into
detail-app[bot] wants to merge 1 commit into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
pieterbeulque
approved these changes
Sep 7, 2026
pieterbeulque
left a comment
Contributor
There was a problem hiding this comment.
Works for me, though refreshing to a 404 is arguably also weird and confusing (but better than a broken checkout?
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Detail bug report: View on Detail
Summary
Related Issue: polarsource/feedback#434
Reload the checkout page when the session expires while the checkout tab is open. Previously the reload was dead code:
Checkout.tsxcheckederror instanceof ClientResponseError, but the checkout providers downgrade that error to a plainErrorResponsebody before re-throwing, so theinstanceofwas alwaysfalseandwindow.location.reload()never ran.What
isExpiredCheckoutError(error)to@polar-sh/checkout(src/utils/errors.ts), re-exported from@polar-sh/checkout/providers. It detects expiry via the thrown body'serror.error === 'ExpiredCheckoutError'discriminator.Checkout.tsx, replace the deaderror instanceof ClientResponseError && error.response.status === 410checks in theupdateandconfirmwrappers withisExpiredCheckoutError(error), and return a never-resolving promise afterwindow.location.reload()so post-reload callers can't race the navigation.errors.test.ts, 20 cases) and the first component test forCheckout.tsx(Checkout.test.tsx, 6 cases covering reload-on-expired, no-reload-on-other-errors, loading-state preservation, and rethrow).Why
A checkout that expired while the tab was left open kept showing a stale UI: confirm did nothing, and seat/unit updates showed a misleading "update failed" error instead of reloading to the expired state.
The bug was a regression introduced when the checkout flow migrated to the internal client:
CheckoutProviderwas changed to catchClientResponseErrorand surface only the plainErrorResponsebody (losing.responseand class identity), whileCheckout.tsxwas changed to checkinstanceof ClientResponseError— a class the new provider layer no longer lets through. The two sides of the change were not reconciled, and no test coveredCheckout.tsx's wrapping layer, so the dead branch survived.How
The fix detects the expired-checkout condition via the discriminator on the thrown body (
error.error === 'ExpiredCheckoutError') — the shape that actually reachesCheckout.tsxafterCheckoutProvider→CheckoutFormProviderre-throw the plain body. The never-resolving promise return afterwindow.location.reload()matters specifically for theconfirmpath: it preventssetFullLoading(false)from briefly clearing the loading indicator before the reload navigates, and keepsupdatecallers (autosave, seat/unit selectors, discount input) from flashing misleading errors. On reload, the server component re-runs and routes the now-expired session tonotFound()via its existing 410 handling.Testing
isExpiredCheckoutErrorand component wiring tests (6) forCheckout.tsxpass; the full@polar-sh/checkoutsuite (460 tests) is green, including the existingCheckoutFormProvidercases that confirm the provider still throws the plainExpiredCheckoutErrorbody with no form error set.@polar-sh/checkoutandweb(web typecheck required rebuilding the checkout package so the re-export lands indist).{"error":"ExpiredCheckoutError","detail":"This checkout session has expired."}body with HTTP 410 forGET,PATCH(the update path), andPOST(the confirm path) on a session forced to expire via the DB — the contract the helper matches.page.tsxis unchanged by this PR.Checkoutand assertswindow.location.reload()is called exactly once with loading state preserved.Checklist
uv run task lint && uv run task lint_types)Automatic Fixes PRs can be configured here.