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
- 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.
- Retry after a transient 5xx/429; the function guards with requestId/currentOrgIdRef so stale results are ignored.
- Verify Stripe configuration server-side if billing should exist.
- 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
- Always send the current org id in ORG_SCOPE_HEADER and refresh it on org switch.
- Configure retry only for transient statuses (429, 5xx).
- Gate the billing UI behind a server capability check (billing enabled).
- The requestId/currentOrgIdRef guards already ignore stale results — keep them when refactoring.
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
- Automation request failed (${result.response.status}).
- Failed to load connection details (${response.status}).
- Failed to start OAuth (${response.status}).
- Seat billing checkout response did not include a URL.
- CUA API error ${response.status}: ${errorText.slice(0, 300)}
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/6443de7a99de3fda.
Report an issue: GitHub.