fix(billing): stop writing empty Autumn records on read failure
Draft
PO
1mo ago+258+258+012345678901234567890123456789-46-46-01234567890123456789
Problem
- A user's Autumn billing record was written with no email, name, or account type — degrading a real billing record.
getAutumnCustomerFieldsopens a Hyperdrive Postgres connection to read five identity columns. A transient Postgres internal error was swallowed and the function returned bare defaults.- Callers then passed those defaults to
getOrCreateAutumnCustomer, which both reads balances and writes identity, so the record was created or touched with onlysource: thinkex. - The drizzle wrapper inlines the bound parameters (including the user id) into the error message, so error tracking groups fragment into one issue per user.
Changes
- Retry then skip, never write empty identity —
getAutumnCustomerFieldsretries the read once, then returnsnullinstead of defaults when it still fails.- Billing panel (
getWorkspaceBillingStateFn): returns no state for that load rather than overwriting the record. - Usage tracking (
trackAutumnUsage): skips the identity write but still records the meter, which carries no identity.
- Billing panel (
- Stable error grouping —
recordOperationalFailurenormalizes a drizzle query error before capture: keep the SQL statement, drop the parameter list, surface the underlying driver cause. This runs on the shared capture path, so any drizzle failure benefits. - Reuse the shared DB helper — the read now goes through
withDbinstead of a hand-rolled connect/dispose.
| Read outcome | Before | After |
|---|---|---|
| Success | Full identity fields | Full identity fields |
| No user row | Defaults | Defaults |
| Transient failure | Defaults → empty identity write | Retry once → null → write skipped |
Why
One user hit a transient Postgres failure while their billing panel loaded, and the graceful-degradation path silently corrupted their Autumn billing record. The cost is small in volume but persistent: every occurrence degrades a real record, and the per-user error titles splinter one fault into many issues as traffic grows.
Testing notes
node_modules/.bin/vp check— format, lint, and types pass.- New unit tests:
src/integrations/autumn/client.server.test.ts(retry recovery,nullon repeated failure, skip-write path) and anormalizeCapturedErrorcase inoperational-error.test.ts. - Full
vp test --run: all pass except a pre-existing, unrelated failure insrc/integrations/posthog/build.test.ts(needs PostHog build env vars; fails identically with these changes stashed).
Created with PostHog Desktop from this inbox report.
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.