{"record":{"id":"751962866b2c8462","repo":"Significant-Gravitas/AutoGPT","slug":"stripe-didn-t-return-a-checkout-url-check-backend","errorCode":null,"errorMessage":"Stripe didn't return a checkout URL. Check backend logs for the underlying Stripe error.","messagePattern":"Stripe didn't return a checkout URL\\. Check backend logs for the underlying Stripe error\\.","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"autogpt_platform/frontend/src/app/(platform)/settings/billing/components/AutomationCreditsTab/BalanceCard/useBalanceCard.ts","lineNumber":71,"sourceCode":"        // ``{\"detail\": \"...\"}``) instead of the generic \"no URL\" message —\n        // the actual failure is almost always a Stripe-side error\n        // (stale customer ID, missing product, invalid API key) that the\n        // user / their team needs to see to fix.\n        const detail =\n          typeof body?.detail === \"string\"\n            ? body.detail\n            : (body?.detail?.msg ?? `Server returned ${status}.`);\n        throw new Error(detail);\n      }\n\n      const url = body?.checkout_url;\n      if (url) {\n        // Navigating away — don't touch React state on the unmounting tree.\n        window.location.href = url;\n        return;\n      }\n\n      throw new Error(\n        \"Stripe didn't return a checkout URL. Check backend logs for the underlying Stripe error.\",\n      );\n    } catch (error) {\n      toast({\n        title: \"Couldn't start checkout\",\n        description:\n          error instanceof Error\n            ? error.message\n            : \"Something went wrong contacting Stripe. Please try again.\",\n        variant: \"destructive\",\n      });\n    }\n  }\n\n  return {\n    balanceCents: balanceCents ?? null,\n    isLoading,\n    isError,","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/Significant-Gravitas/AutoGPT/blob/9c8bb5550f446ba5d3046b78896578742495b3cf/autogpt_platform/frontend/src/app/(platform)/settings/billing/components/AutomationCreditsTab/BalanceCard/useBalanceCard.ts#L53-L89","documentation":"Thrown by the BalanceCard top-up flow when the checkout request returned 2xx but the response body contains no checkout_url. This means the backend explicitly succeeded without producing a Stripe checkout session URL — per the code comment, the underlying reason (Stripe error swallowed, config issue) is only visible in backend logs, which is why the message points there.","triggerScenarios":"POST create-checkout returning 200/201 with {checkout_url: null/undefined/missing} — backend caught a Stripe error and returned a success-shaped response, or a code path forgot to include the URL (e.g. user already has an active session, or an early-return path).","commonSituations":"Backend bug swallowing StripeError into a 200; misconfigured STRIPE_SECRET_KEY making session.create return nothing useful; race where a previous checkout session is reused but the URL isn't propagated.","solutions":["Read backend logs at the moment of the click — the message is designed to send you there; look for swallowed Stripe exceptions.","Verify Stripe env vars on the backend container are set and the key is valid (a quick curl of the Stripe API with the same key confirms).","Inspect the actual 2xx response body in DevTools to see what the backend returned instead of checkout_url.","If it's a backend code path issue, fix the endpoint to fail with 4xx/5xx + detail instead of 200-with-no-URL (the frontend is already prepared to surface detail)."],"exampleFix":"// backend: before\ntry:\n    session = stripe.checkout.Session.create(...)\nexcept stripe.StripeError:\n    pass  # returns 200 with no checkout_url\n\n// backend: after\nexcept stripe.StripeError as e:\n    raise HTTPException(status_code=502, detail=str(e))","handlingStrategy":"validation","validationCode":"function hasCheckoutUrl(body: unknown): body is { checkout_url: string } {\n  return typeof body === \"object\" && body !== null &&\n    typeof (body as any).checkout_url === \"string\" &&\n    (body as any).checkout_url.startsWith(\"https://\");\n}","typeGuard":"function isMissingCheckoutUrl(err: unknown): boolean {\n  return err instanceof Error && err.message.startsWith(\"Stripe didn't return a checkout URL\");\n}","tryCatchPattern":"try {\n  await startCheckout(amount);\n} catch (error) {\n  if (isMissingCheckoutUrl(error)) {\n    // backend-side issue by definition: escalate to operator/backend logs, no user retry helps\n  }\n}","preventionTips":["Treat 200-without-checkout_url as a backend contract violation — fix the endpoint to fail loudly with 4xx/5xx + detail.","Type-guard checkout_url before navigating; don't window.location.assign undefined.","Add backend telemetry on the Stripe session-creation path so 'check backend logs' always has something to find."],"tags":["billing","stripe","checkout","backend-logs","contract-mismatch"],"backgroundTag":null,"analyzedSha":"9c8bb5550f446ba5d3046b78896578742495b3cf","analyzedAt":"2026-08-14T17:17:21.957Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}