{"record":{"id":"be0cc1415cad1adc","repo":"different-ai/openwork","slug":"checkout-response-did-not-include-a-url","errorCode":null,"errorMessage":"Checkout response did not include a URL.","messagePattern":"Checkout response did not include a URL\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ee/apps/den-web/app/(den)/dashboard/_components/inference-screen.tsx","lineNumber":363,"sourceCode":"      setError(\"Only workspace admins can start OpenWork Models checkout.\");\n      return;\n    }\n\n    setError(null);\n    try {\n      await runReauthableAction(\"inference-checkout\", async () => {\n        setSubscribeBusy(true);\n        const { response, payload } = await requestJson(\n          \"/v1/billing/stripe/checkout\",\n          { method: \"POST\", body: JSON.stringify({ type: \"inference\" }) },\n          12000,\n        );\n        if (!response.ok) {\n          throw getRequestError(payload, response, `Checkout failed (${response.status}).`);\n        }\n        const url = payload && typeof payload === \"object\" && \"url\" in payload && typeof payload.url === \"string\" ? payload.url : null;\n        if (!url) {\n          throw new Error(\"Checkout response did not include a URL.\");\n        }\n        window.location.href = url;\n      });\n    } catch (checkoutError) {\n      setError(checkoutError instanceof Error ? checkoutError.message : \"Could not start checkout.\");\n      setSubscribeBusy(false);\n    }\n  }\n\n  async function toggleEnabled() {\n    if (!canManageModels) {\n      setError(\"Only workspace admins can manage OpenWork Models.\");\n      return;\n    }\n    if (!status) return;\n    if (status.enabled || !status.subscribed) {\n      router.push(getBillingRoute(activeOrg?.slug));\n      return;","sourceCodeStart":345,"sourceCodeEnd":381,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/ee/apps/den-web/app/(den)/dashboard/_components/inference-screen.tsx#L345-L381","documentation":"startSubscribeCheckout POSTs to create a Stripe checkout session and expects a JSON payload containing a string `url` field to redirect the browser to. If the response is ok but has no string `url`, this error is thrown because the redirect cannot proceed.","triggerScenarios":"The checkout-session endpoint returns 200 with a body lacking `url` (e.g. {\"sessionId\":...} instead), a null url because Stripe session creation silently failed, or a non-JSON body.","commonSituations":"Billing/Stripe not configured on the server (no price ID or webhook secret), API shape changed after a server upgrade, or the org's billing state prevents session creation but the endpoint still returns 200.","solutions":["Verify Stripe environment variables (secret key, price ID) are configured on the Den server.","Inspect the raw checkout endpoint response to see what shape it actually returns.","Upgrade server and dashboard to matching versions so the response includes `url`.","Check server logs for Stripe API errors during session creation."],"exampleFix":"// before\nconst url = payload && typeof payload === \"object\" && \"url\" in payload && typeof payload.url === \"string\" ? payload.url : null;\nif (!url) throw new Error(\"Checkout response did not include a URL.\");\n// after\nconst url = isRecord(payload) && typeof payload.url === \"string\" && payload.url.startsWith(\"https://\") ? payload.url : null;\nif (!url) throw new Error(`Checkout response did not include a URL. Payload: ${JSON.stringify(payload).slice(0, 200)}`);","handlingStrategy":"type-guard","validationCode":"const data = await res.json();\nif (!(data && typeof data === \"object\" && typeof (data as { url?: unknown }).url === \"string\")) {\n  throw new Error(\"Checkout response did not include a URL.\");\n}","typeGuard":"function hasCheckoutUrl(p: unknown): p is { url: string } {\n  return typeof p === \"object\" && p !== null && \"url\" in p && typeof (p as { url: unknown }).url === \"string\";\n}","tryCatchPattern":"try {\n  await startSubscribeCheckout();\n} catch (e) {\n  setError(e instanceof Error ? e.message : \"Could not start checkout.\");\n  setSubscribeBusy(false);\n}","preventionTips":["Verify Stripe configuration (secret key, price ID) before enabling the subscribe button.","Add a contract test that the checkout endpoint always returns { url: string } on success.","Surface the raw payload in dev builds to debug shape drift quickly.","Keep server and frontend versions in lockstep."],"tags":["stripe","checkout","api-contract"],"backgroundTag":"missing-response-field","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}