{"record":{"id":"245991eecc07af93","repo":"dubinc/dub","slug":"access-token-not-found-for-the-account","errorCode":null,"errorMessage":"Access token not found for the account.","messagePattern":"Access token not found for the account\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/stripe-app/src/utils/oauth.ts","lineNumber":98,"sourceCode":"    throw new Error(\"Failed to fetch user info from Dub.\", {\n      cause: data.error,\n    });\n  }\n\n  const { workspace } = data as { workspace: Workspace };\n\n  return workspace;\n}\n\n// If the token is expired, it will refresh it\nexport async function getValidToken({ stripe }: { stripe: Stripe }) {\n  const token = await getSecret<Token>({\n    stripe,\n    name: \"dub_token\",\n  });\n\n  if (!token) {\n    throw new Error(\"Access token not found for the account.\");\n  }\n\n  try {\n    await getUserInfo({ token });\n  } catch (e) {\n    const refreshedToken = await refreshToken({ token });\n\n    if (!refreshedToken) {\n      console.error(\"Failed to refresh access token.\");\n      return null;\n    }\n\n    await setSecret({\n      stripe,\n      name: \"dub_token\",\n      payload: JSON.stringify(refreshedToken),\n    });\n","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/dubinc/dub/blob/f216b94a24ca5a0a48c6543ee10392c9006c8b75/packages/stripe-app/src/utils/oauth.ts#L80-L116","documentation":"getValidToken loads the stored Dub token from Stripe app secrets (name \"dub_token\"); when no secret exists it throws this message. It means the account never completed (or its data was lost from) the Dub connect flow, so no token is available to validate or refresh.","triggerScenarios":"A Stripe App request path calls getValidToken (via the `token` handler) for an account where getSecret returns null — the user installed the app but never authorized Dub, or the secret was deleted.","commonSituations":"Stripe App installed but OAuth connect step skipped; secrets cleared by reinstall; calling Dub-backed features from a different Stripe account than the one that connected.","solutions":["Complete the Dub connect/OAuth flow in the Stripe App so dub_token is stored as a secret.","Verify you are operating on the same Stripe account that performed the connection.","Reinstall/re-authorize the Stripe App if secrets were wiped.","Add a UX guard: check for the token first and show 'Connect Dub' instead of a raw error."],"exampleFix":"// before\nconst token = await getValidToken(stripe); // throws for unconnected accounts\n// after\nconst stored = await getSecret({ stripe, name: \"dub_token\" });\nif (!stored) return showConnectOnboarding();","handlingStrategy":"validation","validationCode":"const stored = await getSecret<Token>({ stripe, name: \"dub_token\" });\nif (!stored) {\n  // render onboarding instead of calling Dub-dependent handlers\n  return respondWith({ connectUrl: buildDubConnectUrl(stripeAccountId) });\n}","typeGuard":"function isStoredToken(v: unknown): v is { access_token: string; refresh_token: string } {\n  return typeof v === \"object\" && v !== null && typeof (v as any).access_token === \"string\" && typeof (v as any).refresh_token === \"string\";\n}","tryCatchPattern":"try {\n  const token = await getValidToken({ stripe });\n} catch (e) {\n  if ((e as Error).message === \"Access token not found for the account.\") {\n    return startDubOnboarding(stripeAccountId); // friendly connect flow\n  }\n  throw e;\n}","preventionTips":["Gate all Dub features behind a 'connected?' check that reads the secret first.","Persist dub_token during install; block feature use until the OAuth flow completes.","Handle account switching — secrets are per Stripe account.","Warn users on reinstall that reconnection is required."],"tags":["stripe","secrets","authentication","oauth"],"backgroundTag":"missing-oauth-token","analyzedSha":"f216b94a24ca5a0a48c6543ee10392c9006c8b75","analyzedAt":"2026-08-31T18:35:50.395Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}