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

  1. Inspect the actual /v1/billing response body and compare against what parseStripeBilling expects; log the payload before parsing to spot missing fields.
  2. Verify the Den server's Stripe configuration (API keys, webhook setup) is complete so billing data is fully populated.
  3. Re-run refreshStripeBilling — stale org switches are guarded by currentOrgIdRef/billingRequestIdRef, but a transient partial response can be retried.
  4. 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

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


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/c1959763fdc2dbaf. Report an issue: GitHub.