{"record":{"id":"16878e9e13e2bd37","repo":"overleaf/overleaf","slug":"this-account-has-no-billing-info","errorCode":null,"errorMessage":"This account has no billing info","messagePattern":"This account has no billing info","errorType":"exception","errorClass":"MissingBillingInfoError","httpStatus":null,"severity":"warning","filePath":"services/web/app/src/Features/Subscription/RecurlyClient.mjs","lineNumber":542,"sourceCode":" */\nasync function resumeSubscriptionByUuid(subscriptionUuid) {\n  return await client.resumeSubscription('uuid-' + subscriptionUuid)\n}\n\n/**\n * Get the payment method for the given user\n *\n * @param {string} userId\n * @return {Promise<PaymentMethod>}\n */\nasync function getPaymentMethod(userId) {\n  let billingInfo\n\n  try {\n    billingInfo = await client.getBillingInfo(`code-${userId}`)\n  } catch (error) {\n    if (error instanceof recurly.errors.NotFoundError) {\n      throw new MissingBillingInfoError('This account has no billing info', {\n        userId,\n      })\n    }\n    throw error\n  }\n\n  return paymentMethodFromApi(billingInfo)\n}\n\n/**\n * Get the configuration for a given add-on\n *\n * @param {string} planCode\n * @param {string} addOnCode\n * @return {Promise<PaymentProviderAddOn>}\n */\nasync function getAddOn(planCode, addOnCode) {\n  const addOn = await client.getPlanAddOn(","sourceCodeStart":524,"sourceCodeEnd":560,"githubUrl":"https://github.com/overleaf/overleaf/blob/28ad3b03b71cb4311decdcb55c36b33ec10d72db/services/web/app/src/Features/Subscription/RecurlyClient.mjs#L524-L560","documentation":"RecurlyClient.getPaymentMethod fetches a user's billing info from Recurly. If Recurly returns a NotFoundError for the account's billing info, it throws MissingBillingInfoError — the Recurly account exists the call targets but has no payment method stored on file.","triggerScenarios":"Calling getPaymentMethod(userId) for a Recurly account that has never saved a card/PayPal/billing address, e.g. a user who subscribed via a path that skipped billing-info collection.","commonSituations":"Group-plan admins who joined but never entered payment details; accounts migrated between providers losing billing info; SSO-created accounts; flows assuming a card on file after plan change.","solutions":["Catch MissingBillingInfoError and redirect the user to add billing info before continuing","Check whether the user has billing info on file (via this call guarded by try/catch) before operations requiring a payment method","Catch the underlying recurly.errors.NotFoundError if handling at the Recurly client layer","Verify the userId maps to the correct Recurly account code (code-<userId>) to rule out a lookup against the wrong account"],"exampleFix":"// before\nconst paymentMethod = await recurlyClient.getPaymentMethod(userId)\n// after\nlet paymentMethod\ntry {\n  paymentMethod = await recurlyClient.getPaymentMethod(userId)\n} catch (err) {\n  if (err instanceof MissingBillingInfoError) {\n    return { paymentMethod: null, needsBillingInfo: true }\n  }\n  throw err\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const pm = await recurlyClient.getPaymentMethod(userId)\n} catch (err) {\n  if (err instanceof MissingBillingInfoError) {\n    return { paymentMethod: null, needsBillingInfo: true }\n  }\n  throw err\n}","preventionTips":["Treat missing billing info as a normal state, not a crash","Route users without a payment method to the billing-info form first","Verify userId maps to the correct Recurly account code","Cache billing-info presence to reduce NotFound round-trips"],"tags":["subscription","recurly","billing-info","not-found"],"backgroundTag":"missing-billing-info","analyzedSha":"28ad3b03b71cb4311decdcb55c36b33ec10d72db","analyzedAt":"2026-09-03T02:10:22.807Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T07:17:11.731Z"}