different-ai/openwork · error · Error
Billing response was incomplete.
Error message
Billing response was incomplete.
What it means
refreshStripeBilling fetches /v1/billing and passes the payload to parseStripeBilling; the error is thrown when the parser returns null, i.e. the response body is JSON but lacks the required Stripe billing fields (e.g. subscription/customer data). The API returned 200 OK but the payload shape was not a complete billing record, so the dashboard refuses to render partial data.
Source
Thrown at ee/apps/den-web/app/(den)/dashboard/_components/billing-dashboard-screen.tsx:204
);
const canManageBillingSettings = access.canManageSettings;
async function refreshStripeBilling(quiet = false) {
const expectedOrgId = activeOrgId;
if (!expectedOrgId) return null;
const requestId = billingRequestIdRef.current + 1;
billingRequestIdRef.current = requestId;
setStripeBusy(true);
if (!quiet) setStripeError(null);
try {
const { response, payload } = await requestJson(
"/v1/billing",
{ method: "GET", headers: { [ORG_SCOPE_HEADER]: expectedOrgId } },
12000,
);
if (!response.ok) throw new Error(getErrorMessage(payload, `Billing lookup failed (${response.status}).`));
const parsed = parseStripeBilling(payload);
if (!parsed) throw new Error("Billing response was incomplete.");
if (currentOrgIdRef.current !== expectedOrgId || billingRequestIdRef.current !== requestId) return null;
setStripeBillingValue(parsed);
setStripeBillingOrgId(expectedOrgId);
setPolarBilling(parsePolarBilling(payload));
return parsed;
} catch (error) {
if (!quiet && currentOrgIdRef.current === expectedOrgId && billingRequestIdRef.current === requestId) {
setStripeError(error instanceof Error ? error.message : "Could not load billing details.");
}
return null;
} finally {
if (currentOrgIdRef.current === expectedOrgId && billingRequestIdRef.current === requestId) setStripeBusy(false);
}
}
useEffect(() => {
if (!sessionHydrated || !user) return;
void refreshStripeBilling(false);View on GitHub (pinned to 2b7df46e8a)
Solutions
- Inspect the actual /v1/billing response body and compare against what parseStripeBilling expects; log the payload before parsing to spot missing fields.
- Verify the Den server's Stripe configuration (API keys, webhook setup) is complete so billing data is fully populated.
- Re-run refreshStripeBilling — stale org switches are guarded by currentOrgIdRef/billingRequestIdRef, but a transient partial response can be retried.
- Upgrade the web app and Den server together so the billing schema versions match.
Example fix
// before
const parsed = parseStripeBilling(payload);
if (!parsed) throw new Error("Billing response was incomplete.");
// after
const parsed = parseStripeBilling(payload);
if (!parsed) {
console.warn("billing payload missing expected fields", payload);
throw new Error(`Billing response was incomplete: ${JSON.stringify(Object.keys(payload ?? {}))}`);
} Defensive patterns
Strategy: type-guard
Validate before calling
function looksLikeStripeBilling(p: unknown): boolean {
return typeof p === "object" && p !== null && "subscription" in p;
}
// call before setStripeBillingValue: if (!looksLikeStripeBilling(payload)) return; Type guard
function isStripeBilling(p: unknown): p is StripeBilling {
return typeof p === "object" && p !== null && "subscription" in p && "customer" in p;
} Try / catch
try {
const parsed = await refreshStripeBilling(orgId);
if (parsed === null) return; // stale request, ignore
} catch (error) {
const msg = error instanceof Error ? error.message : "Billing lookup failed";
setStripeError(msg);
} Prevention
- Log the raw /v1/billing payload when parsing fails to speed diagnosis
- Keep client parseStripeBilling and the server billing handler in the same package/types
- Add a server-side contract test asserting the billing endpoint returns all fields parseStripeBilling requires
- Retry transient failures with backoff before surfacing the error to the user
When it happens
Trigger: GET /v1/billing returns 200 with a body that parseStripeBilling cannot validate: missing subscription object, wrong field types, an empty object, or the server returning a stubbed/degraded payload during Stripe outage or misconfiguration on the Den server.
Common situations: Den server not fully configured with Stripe keys so the endpoint returns a partial payload; API version drift between server and web app changing the billing response schema; a proxy or gateway returning a valid-JSON but non-billing body (e.g. an error envelope with status 200).
Related errors
- Seat checkout response did not include a URL.
- Billing portal response did not include a URL.
- invalid_billing_payload
- Invalid cloud provider sync response.
- Invalid cloud provider sync status.
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/c1959763fdc2dbaf.
Report an issue: GitHub.