dubinc/dub · error
Failed to fetch user info from Dub.
Error message
Failed to fetch user info from Dub.
What it means
getUserInfo calls Dub's /workspaces endpoint with the stored access token to fetch the user's workspace; if the response is not OK it throws this fixed message with the API error attached as `cause`. It is used to validate that a stored token still works before returning it from getValidToken.
Source
Thrown at packages/stripe-app/src/utils/oauth.ts:80
return data as Token;
} catch (e) {
console.error("Unable to retrieve Dub access token:", (e as Error).message);
}
}
// Returns the user info from Dub using the access token
export async function getUserInfo({ token }: { token: Token }) {
const response = await fetch(`${DUB_API_HOST}/oauth/userinfo`, {
headers: {
Authorization: `Bearer ${token.access_token}`,
},
});
const data = await response.json();
if (!response.ok) {
throw new Error("Failed to fetch user info from Dub.", {
cause: data.error,
});
}
const { workspace } = data as { workspace: Workspace };
return workspace;
}
// If the token is expired, it will refresh it
export async function getValidToken({ stripe }: { stripe: Stripe }) {
const token = await getSecret<Token>({
stripe,
name: "dub_token",
});
if (!token) {
throw new Error("Access token not found for the account.");View on GitHub (pinned to f216b94a24)
Solutions
- Inspect error.cause for the API status/message (typically 401 unauthorized).
- Trigger token refresh: getValidToken already catches this and calls refreshToken — ensure that path runs.
- If refresh also fails, have the user reconnect Dub from the Stripe App to issue a new token.
- Check Dub API status if the cause indicates 5xx/network issues.
Example fix
// before
// token assumed valid every call
// after
try { await getUserInfo({ token }); } catch { token = await refreshToken({ token }); } Defensive patterns
Strategy: retry
Validate before calling
const token = await getSecret({ stripe, name: "dub_token" });
if (!token?.access_token) throw new Error("no Dub token stored; run the connect flow first"); Type guard
function isWorkspacePayload(data: unknown): data is { workspace: { id: string } } {
return typeof data === "object" && data !== null && "workspace" in data && typeof (data as any).workspace?.id === "string";
} Try / catch
try {
const info = await getUserInfo({ token });
} catch (e) {
if (e instanceof Error && e.cause && /unauthorized|401/i.test(JSON.stringify(e.cause))) {
const fresh = await refreshToken({ token });
return getUserInfo({ token: fresh });
}
if (e instanceof Error && e.cause && /5\d\d/.test(JSON.stringify(e.cause))) {
await sleep(1000); return getUserInfo({ token }); // retry transient
}
throw e;
} Prevention
- Always route token use through getValidToken so invalid tokens trigger refresh.
- Treat 401 as refresh-then-reconnect, not fatal.
- Retry only 5xx/network causes; 401/403 need new credentials.
- Surface reconnect instructions to the user when refresh also fails.
When it happens
Trigger: getValidToken → getUserInfo with an access token that the Dub API rejects (revoked, expired, or malformed) or when the workspace fetch request otherwise fails (network/5xx).
Common situations: User revoked Dub's access from their Dub workspace settings; token fetched from Stripe secrets was saved under an older OAuth scope; Dub API incident during a Stripe App session/token call.
Related errors
- Failed to update workspace.
- data.error.message
- Access token not found for the account.
- (parsedData as APIError).error.message
- textData
AI-assisted analysis of dubinc/dub@f216b94a24 (2026-08-31).
Data as JSON: /api/errors/21965b7ce95f876d.
Report an issue: GitHub.