different-ai/openwork · error · Error

Billing lookup failed (${response.status}).

Error message

Billing lookup failed (${response.status}).

What it means

refreshStripeBilling GETs /v1/billing with an org-scope header and a 12s timeout. If response.ok is false, this error is thrown with the server message or a status fallback. It means the billing endpoint rejected the request, so no Stripe/Polar billing state is refreshed.

Source

Thrown at ee/apps/den-web/app/(den)/dashboard/_components/billing-dashboard-screen.tsx:202

    orgContext?.currentMember.isOwner ?? false,
    orgContext?.roles,
  );
  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(() => {

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Check the status: 401 → re-authenticate; 403 → verify org membership and that expectedOrgId matches the current org; 404 → confirm billing is enabled on this Den server.
  2. Retry after a transient 5xx/429; the function guards with requestId/currentOrgIdRef so stale results are ignored.
  3. Verify Stripe configuration server-side if billing should exist.
  4. Sign out/in to refresh session cookies if 401 persists.

Example fix

// before
if (!response.ok) throw new Error(getErrorMessage(payload, `Billing lookup failed (${response.status}).`));
// after
if (response.status === 403) throw new Error("You don't have access to billing for this organization.");
if (!response.ok) throw new Error(getErrorMessage(payload, `Billing lookup failed (${response.status}).`));
Defensive patterns

Strategy: retry

Validate before calling

// Verify org scope header matches the active org before fetching billing
if (!expectedOrgId) return null; // skip billing fetch
const membershipOk = await checkOrgMembership(expectedOrgId);
if (!membershipOk) return null;

Type guard

function isStripeBilling(v: unknown): boolean {
  return typeof v === "object" && v !== null && "subscription" in v;
}

Try / catch

try {
  await refreshStripeBilling(orgId);
} catch (err) {
  const m = err instanceof Error ? err.message : "";
  if (m.includes("(401)")) redirectToSignIn();
  else if (m.includes("(403)")) showToast("No billing access for this organization.");
  else if (/\((5\d\d|429)\)/.test(m)) setTimeout(() => refreshStripeBilling(orgId), 5000);
  else showToast(m || "Billing lookup failed");
}

Prevention

When it happens

Trigger: GET /v1/billing returns 401 (expired session), 403 (missing/incorrect ORG_SCOPE_HEADER org membership), 404 (billing not enabled/self-hosted without billing), 429, or 5xx; expectedOrgId is stale after an org switch.

Common situations: User switched organizations and the ref carries an org they no longer belong to; Stripe not configured on the server; billing feature flag disabled; transient backend error.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


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