diff --git a/assets/pr2950-capacity-expiry.png b/assets/pr2950-capacity-expiry.png
new file mode 100644
index 0000000000..10e3c995b9
Binary files /dev/null and b/assets/pr2950-capacity-expiry.png differ
diff --git a/gui/src/components/provider-workspace/ProviderCapacityQuota.tsx b/gui/src/components/provider-workspace/ProviderCapacityQuota.tsx
index abc2fb9e63..20907b8f6c 100644
--- a/gui/src/components/provider-workspace/ProviderCapacityQuota.tsx
+++ b/gui/src/components/provider-workspace/ProviderCapacityQuota.tsx
@@ -56,18 +56,30 @@ export function ProviderCapacityQuota({ report, pending }: { report: ProviderQuo
...(aggregation.customWindows ?? []).map((window, index) => ({ key: index + 3, label: window.label, window })),
] : [];
const formatPercent = (value: number) => new Intl.NumberFormat(locale, { maximumFractionDigits: 1 }).format(value);
- const formatRecoveryAt = (value: number) => new Intl.DateTimeFormat(locale, {
- dateStyle: "medium",
- timeStyle: "short",
- }).format(new Date(value > 10_000_000_000 ? value : value * 1000));
+ // `Intl.DateTimeFormat.format()` throws a RangeError on a time value outside ±8.64e15 ms.
+ // These timestamps come from provider APIs and persisted cache, so one unrepresentable value
+ // would take down the whole capacity panel. Resolve to null and omit the line instead.
+ const asDate = (value: number): Date | null => {
+ const date = new Date(value > 10_000_000_000 ? value : value * 1000);
+ return Number.isFinite(date.getTime()) ? date : null;
+ };
+ const formatRecoveryAt = (value: number) => {
+ const date = asDate(value);
+ return date === null ? null : new Intl.DateTimeFormat(locale, {
+ dateStyle: "medium",
+ timeStyle: "short",
+ }).format(date);
+ };
const localeTag = bcp47(locale);
const formatCredits = (value: number) => new Intl.NumberFormat(localeTag, {
style: "currency",
currency: "USD",
}).format(value);
- const formatPeriodEnd = (value: number) => new Intl.DateTimeFormat(localeTag, {
- dateStyle: "medium",
- }).format(new Date(value > 10_000_000_000 ? value : value * 1000));
+ const formatPeriodEnd = (value: number) => {
+ const date = asDate(value);
+ return date === null ? null : new Intl.DateTimeFormat(localeTag, { dateStyle: "medium" }).format(date);
+ };
+ const periodEnd = credits?.expiresAt === undefined ? null : formatPeriodEnd(credits.expiresAt);
return (
<>
@@ -91,19 +103,20 @@ export function ProviderCapacityQuota({ report, pending }: { report: ProviderQuo
{formatCredits(credits.remaining)}
)}
- {credits?.expiresAt !== undefined && (
+ {periodEnd !== null && (
- {t("quota.creditsPeriodEnds", { date: formatPeriodEnd(credits.expiresAt) })}
+ {t("quota.creditsPeriodEnds", { date: periodEnd })}
)}
- {recoveryRows.flatMap(({ key, label, window }) => (
- window.nextRecoveryAt !== undefined && window.nextRecoveryPercent !== undefined
+ {recoveryRows.flatMap(({ key, label, window }) => {
+ const recoveryAt = window.nextRecoveryAt === undefined ? null : formatRecoveryAt(window.nextRecoveryAt);
+ return recoveryAt !== null && window.nextRecoveryPercent !== undefined
? [
- {t("pws.capacity.nextRecovery")} · {label} · {formatRecoveryAt(window.nextRecoveryAt)}
+ {t("pws.capacity.nextRecovery")} · {label} · {recoveryAt}
{t("pws.capacity.recoveryShare", { percent: formatPercent(window.nextRecoveryPercent) })}
]
- : []
- ))}
+ : [];
+ })}
{showsAggregate && aggregation && aggregation.currentAccount?.quota && (
diff --git a/gui/src/provider-workspace/report.ts b/gui/src/provider-workspace/report.ts
index 539fbba2f2..4d4ff53f3b 100644
--- a/gui/src/provider-workspace/report.ts
+++ b/gui/src/provider-workspace/report.ts
@@ -39,6 +39,22 @@ const finite = (value: unknown): number | undefined => (
typeof value === "number" && Number.isFinite(value) ? value : undefined
);
+/**
+ * A finite number is not necessarily a representable date.
+ *
+ * Reports are also read from persisted cache, so a bogus expiry recorded before the wire-side
+ * guard existed still reaches the GUI. `Intl.DateTimeFormat.format()` throws a RangeError on an
+ * out-of-range time value rather than rendering it, which turns one bad provider field into a
+ * render fault for the whole capacity panel. Drop the field instead: the rest of the credit
+ * figures stay useful without it.
+ */
+const dateTimestamp = (value: unknown): number | undefined => {
+ const timestamp = finite(value);
+ if (timestamp === undefined) return undefined;
+ const milliseconds = timestamp > 10_000_000_000 ? timestamp : timestamp * 1000;
+ return Number.isFinite(new Date(milliseconds).getTime()) ? timestamp : undefined;
+};
+
function quotaFromUnknown(quota: unknown, fallbackUpdatedAt?: number): AccountQuota | null {
if (!quota || typeof quota !== "object" || Array.isArray(quota)) return null;
const q = quota as Record;
@@ -61,7 +77,7 @@ function quotaFromUnknown(quota: unknown, fallbackUpdatedAt?: number): AccountQu
const creditsLimit = finite(creditsRaw?.limit);
const creditsRemaining = finite(creditsRaw?.remaining);
const creditsPercent = finite(creditsRaw?.percent);
- const creditsExpiresAt = finite(creditsRaw?.expiresAt);
+ const creditsExpiresAt = dateTimestamp(creditsRaw?.expiresAt);
const creditsUsd = creditsUsed !== undefined
&& creditsLimit !== undefined
&& creditsRemaining !== undefined
diff --git a/gui/tests/provider-capacity-credits.test.tsx b/gui/tests/provider-capacity-credits.test.tsx
index 35045aa870..38035c97c3 100644
--- a/gui/tests/provider-capacity-credits.test.tsx
+++ b/gui/tests/provider-capacity-credits.test.tsx
@@ -39,3 +39,13 @@ test("credits with an expiry render the localized billing-period end date", () =
expect(markup).toContain("Credits balance");
expect(markup).toContain("Billing period ends 26 Aug 2026");
});
+
+// Rendering is the failure point, not just the value: Intl.DateTimeFormat.format() throws a
+// RangeError on a time value outside ±8.64e15 ms, so an unrepresentable expiry took down the
+// whole capacity panel rather than showing a wrong date.
+test("credits with an unrepresentable expiry still render the balance", () => {
+ const markup = renderCredits(1e20);
+ expect(markup).toContain("Credits balance");
+ expect(markup).toContain("US$37.50");
+ expect(markup).not.toContain("Billing period ends");
+});
diff --git a/gui/tests/provider-capacity.test.ts b/gui/tests/provider-capacity.test.ts
index 3916b3eadb..5911e89d61 100644
--- a/gui/tests/provider-capacity.test.ts
+++ b/gui/tests/provider-capacity.test.ts
@@ -61,6 +61,18 @@ test("provider quota reports reject malformed required credits and drop malforme
creditsUsd: { used: 12.5, limit: 50, remaining: 37.5, percent: 25 },
updatedAt: 123,
});
+
+ // Persisted reports predate the wire-side guard, so normalization is a second line of
+ // defence: an unrepresentable expiry must be dropped rather than handed to a formatter.
+ expect(accountQuotaFromReport({
+ updatedAt: 123,
+ quota: {
+ creditsUsd: { used: 12.5, limit: 50, remaining: 37.5, percent: 25, expiresAt: 1e20 },
+ },
+ })).toEqual({
+ creditsUsd: { used: 12.5, limit: 50, remaining: 37.5, percent: 25 },
+ updatedAt: 123,
+ });
});
test("capacity metadata preserves estimate, raw current quota, recovery percent, and incomplete coverage", () => {
diff --git a/src/providers/quota-wire.ts b/src/providers/quota-wire.ts
index 0abcb69efe..31a0c81457 100644
--- a/src/providers/quota-wire.ts
+++ b/src/providers/quota-wire.ts
@@ -30,7 +30,12 @@ export const QUOTA_JSON_READ_FAILURE = Symbol("quota-json-read-failure");
/** Unix 0 / negative values are sentinels, not reset clocks (Command Code fiveHour.resetAt: 0). */
export function epochMillis(value: number): number | undefined {
if (!Number.isFinite(value) || value <= 0) return undefined;
- return value > 10_000_000_000 ? value : value * 1000;
+ const milliseconds = value > 10_000_000_000 ? value : value * 1000;
+ // A finite number is not necessarily a representable date. ECMAScript caps time values at
+ // ±8.64e15 ms, and `Intl.DateTimeFormat.format()` throws a RangeError past that instead of
+ // rendering something wrong. A provider that reports a bogus expiry must not become a
+ // rendering fault in every consumer that formats it.
+ return Number.isFinite(new Date(milliseconds).getTime()) ? milliseconds : undefined;
}
export function normalizeResetAt(value: unknown): number | undefined {
diff --git a/src/providers/quota.ts b/src/providers/quota.ts
index 98bd079119..e27bb29a31 100644
--- a/src/providers/quota.ts
+++ b/src/providers/quota.ts
@@ -1153,14 +1153,17 @@ export function parseXaiCreditsResponse(value: unknown): { percent: number; rese
if (!config) return null;
const period = asRecord(config.currentPeriod);
if (!period || period.type !== "USAGE_PERIOD_TYPE_WEEKLY") return null;
- const resetAt = normalizeResetAt(period.end);
- if (resetAt === undefined) return null;
+ let percent = 0;
if (config.creditUsagePercent !== undefined) {
- const percent = normalizePercent(config.creditUsagePercent);
- if (percent === undefined) return null;
- return { percent, resetAt };
+ const normalized = normalizePercent(config.creditUsagePercent);
+ if (normalized === undefined) return null;
+ percent = normalized;
}
- return { percent: 0, resetAt };
+ const resetAt = normalizeResetAt(period.end);
+ return {
+ percent,
+ ...(resetAt !== undefined ? { resetAt } : {}),
+ };
}
async function fetchXaiWeeklyCredits(accessToken: string, userId: string): Promise {
diff --git a/tests/command-code-quota.test.ts b/tests/command-code-quota.test.ts
index 3e8da6d0d3..8ba447baaf 100644
--- a/tests/command-code-quota.test.ts
+++ b/tests/command-code-quota.test.ts
@@ -364,6 +364,38 @@ describe("Command Code provider quota", () => {
});
});
+ // An out-of-range period end is not merely a wrong date: every consumer that formats it
+ // through Intl throws a RangeError. Drop the field and keep the usable credit figures.
+ test("an out-of-range subscription period end is dropped, not carried into the report", async () => {
+ globalThis.fetch = (async (input: RequestInfo | URL) => {
+ const url = String(input);
+ const body = url.includes("/alpha/whoami")
+ ? {}
+ : url.includes("/alpha/billing/subscriptions")
+ ? { data: { currentPeriodStart: "2026-08-01T00:00:00.000Z", currentPeriodEnd: 1e20 } }
+ : url.includes("/alpha/usage/summary")
+ ? { totalCost: 12 }
+ : {
+ credits: { monthlyCredits: 0, purchasedCredits: 0, freeCredits: 0 },
+ windowLimits: { fiveHour: { cap: 100, used: 40 } },
+ };
+ return new Response(JSON.stringify(body), { status: 200, headers: { "content-type": "application/json" } });
+ }) as typeof fetch;
+
+ const result = await fetchProviderQuotaReports(commandCodeConfig(), true);
+
+ expect(result.reports[0]?.quota).toEqual({
+ fiveHourPercent: 40,
+ creditsUsd: {
+ used: 12,
+ limit: 12,
+ remaining: 0,
+ percent: 100,
+ },
+ updatedAt: expect.any(Number),
+ });
+ });
+
test("a mixed balance with roll-over purchased credits carries no subscription expiry", async () => {
globalThis.fetch = (async (input: RequestInfo | URL) => {
const url = String(input);
diff --git a/tests/provider-quota.test.ts b/tests/provider-quota.test.ts
index 6d0e395f3d..5eef964306 100644
--- a/tests/provider-quota.test.ts
+++ b/tests/provider-quota.test.ts
@@ -2393,6 +2393,12 @@ describe("fetchProviderQuotaReports", () => {
percent: 0,
resetAt: Date.parse("2026-08-15T13:05:52.277209Z"),
});
+ expect(parseXaiCreditsResponse({
+ config: {
+ creditUsagePercent: 42,
+ currentPeriod: { type: "USAGE_PERIOD_TYPE_WEEKLY", end: 1e20 },
+ },
+ })).toEqual({ percent: 42 });
expect(parseXaiCreditsResponse({
config: {
creditUsagePercent: 10,