Skip to content

fix(limits): handle missing billing limit fields from staging API#306

Merged
ben-fornefeld merged 1 commit intomainfrom
fix/limits-nullability-case
Apr 29, 2026
Merged

fix(limits): handle missing billing limit fields from staging API#306
ben-fornefeld merged 1 commit intomainfrom
fix/limits-nullability-case

Conversation

@ben-fornefeld
Copy link
Copy Markdown
Member

@ben-fornefeld ben-fornefeld commented Apr 28, 2026

Summary

  • Coerces missing limit_amount_gte / alert_amount_gte fields from the billing API to null at the repository boundary so the rest of the limits page can rely on the typed number | null contract.
  • Fixes a client-side Cannot read properties of undefined (reading 'toLocaleString') crash on /dashboard/[teamSlug]/limits for any team that has never set a usage limit or alert.

Why this happened

The billing API's BillingLimit struct uses *int64 with json:",omitempty", so unset limits are omitted from the JSON response entirely, not serialized as explicit null. The recent limits page refactor (#296) replaced the previous originalValue?.toLocaleString() / ?? '' pattern with strict value !== null checks followed by formatNumber(value) — which lets undefined slip through and throws inside toLocaleString.

I verified two weeks of e2b-dev/belt history (#654 sqlc migration, #690 SupabaseDB cleanup, #693 oapi-codegen upgrade); none of them changed the BillingLimit JSON shape. The API has always omitted unset fields — the dashboard refactor is what removed the tolerance for undefined.

The billing API serializes BillingLimit with `omitempty` on its `*int64`
pointer fields, so unset limits are absent from the JSON response rather
than emitted as explicit nulls. The post-refactor limits page guards
with strict `value !== null` checks and then calls `formatNumber(value)`
(which calls `toLocaleString`), so an `undefined` field slipped through
and crashed the page with "Cannot read properties of undefined". Coerce
both fields at the repository boundary so the rest of the app can keep
relying on the `number | null` contract.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 28, 2026 22:44
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 28, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
web Error Error Apr 28, 2026 10:45pm
web-juliett Ready Ready Preview, Comment Apr 28, 2026 10:45pm

Request Review

@cursor
Copy link
Copy Markdown

cursor Bot commented Apr 28, 2026

PR Summary

Low Risk
Low risk: small, isolated change in the billing repository to normalize API responses; main risk is masking unexpected backend schema changes for billing limits.

Overview
Normalizes the getLimits() repository response to tolerate staging/legacy billing API behavior where unset limit fields are omitted from JSON.

Instead of returning the raw BillingLimit payload, it now parses a Partial<BillingLimit> and coerces missing limit_amount_gte/alert_amount_gte to explicit null, preventing downstream UI code from receiving undefined.

Reviewed by Cursor Bugbot for commit b8393b5. Bugbot is set up for automated code reviews on this repo. Configure here.

@ben-fornefeld ben-fornefeld added the bug Something isn't working label Apr 28, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens the billing limits repository so the dashboard limits page can rely on a strict number | null contract even when the billing API omits unset fields (instead of sending explicit null), preventing a client-side crash when formatting missing values.

Changes:

  • Coerce missing limit_amount_gte and alert_amount_gte fields from the billing API response to null in getLimits().

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

LGTM — small, type-correct boundary fix that aligns the API response with the existing BillingLimit interface.

Extended reasoning...

Overview

Single-file change to src/core/modules/billing/repository.server.ts: getLimits() now parses the response as Partial<BillingLimit> and coerces missing limit_amount_gte/alert_amount_gte fields to null via ?? null. The BillingLimit interface already declares these as number | null, so this just makes the runtime payload match the typed contract.

Security risks

None. No auth, crypto, or permission paths are touched. The data flow is unchanged — only nullability normalization at the repository boundary. No new untrusted input is parsed or trusted.

Level of scrutiny

Low. This is a defensive 4-line fix for a documented toLocaleString crash introduced when #296 tightened the limits page to assume strict number | null. The Go backend uses *int64 with omitempty, so omitted fields surface as undefined in JS — coercing to null is the right and minimal fix.

Other factors

The downstream consumers (usage-limit-form, usage-alert-form, remove-usage-limit-dialog) all type the cache as BillingLimit | undefined with number | null fields, so this fix removes a real type/runtime mismatch without changing any consumer behavior. No bugs surfaced from the bug hunting system.

@ben-fornefeld ben-fornefeld merged commit 75caad0 into main Apr 29, 2026
16 checks passed
@ben-fornefeld ben-fornefeld deleted the fix/limits-nullability-case branch April 29, 2026 00:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants