dubinc/dub · error
data.error.message
Error message
data.error.message
What it means
getToken exchanges the Stripe account's OAuth code with Dub for an access token; on a non-OK response it throws with the message from the OAuth provider's error payload. Note the outer catch only logs the message ("Unable to retrieve Dub access token: ...") and swallows the error, returning undefined to callers.
Source
Thrown at packages/stripe-app/src/utils/oauth.ts:60
try {
const response = await fetch(`${DUB_API_HOST}/oauth/token`, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
client_id: DUB_CLIENT_ID,
redirect_uri: getRedirectUrl(mode),
grant_type: "authorization_code",
code_verifier: verifier,
code,
}),
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error.message);
}
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();
View on GitHub (pinned to f216b94a24)
Solutions
- Check the logged message ("Unable to retrieve Dub access token: ...") for the provider's error (e.g. invalid_grant).
- Have the user restart the connect flow to get a fresh authorization code (codes are single-use).
- Verify DUB_CLIENT_ID / DUB_CLIENT_SECRET and the registered redirect URI match the deployed Stripe App.
- Fix the code path so getToken's throw isn't swallowed and callers handle undefined tokens.
Example fix
// before
} catch (e) { console.error("Unable to retrieve Dub access token:", (e as Error).message); }
// after
} catch (e) { console.error(...); throw e; } // propagate so caller can prompt reconnect Defensive patterns
Strategy: try-catch
Validate before calling
if (!process.env.DUB_CLIENT_ID || !process.env.DUB_CLIENT_SECRET) {
throw new Error("DUB_CLIENT_ID and DUB_CLIENT_SECRET must be configured");
}
if (!code || typeof code !== "string") throw new Error("OAuth code missing from connect callback"); Type guard
function isTokenResponse(data: unknown): data is { access_token: string; refresh_token: string } {
return typeof data === "object" && data !== null && "access_token" in data;
} Try / catch
try {
const token = await getToken({ code });
if (!token) throw new Error("token exchange returned nothing — check logs for the underlying error");
await saveSecret({ stripe, name: "dub_token", value: token });
} catch (e) {
console.error("Dub connect failed:", (e as Error).message);
return showReconnectUi();
} Prevention
- Never reuse authorization codes — always start a fresh connect flow.
- Verify redirect URI in Dub's OAuth app matches the deployed Stripe App.
- Keep client credentials in env/secrets, rotated in sync with deployments.
- Change getToken's catch to rethrow so failures are observable.
When it happens
Trigger: The Dub OAuth token endpoint returns 4xx/5xx during the Stripe App install/connect flow: invalid or already-consumed authorization `code`, client id/secret mismatch, or redirect_uri mismatch.
Common situations: User refreshes the connect page causing code reuse; DUB_CLIENT_SECRET env misconfigured in the Stripe App deployment; redirect URI registered in Dub dev portal doesn't match the deployed one.
Related errors
- Access token not found for the account.
- Failed to fetch user info from Dub.
- Access token not found. Please run `dub login` to authentica
- Failed to update workspace.
- Not found
AI-assisted analysis of dubinc/dub@f216b94a24 (2026-08-31).
Data as JSON: /api/errors/5e12c7f91047f027.
Report an issue: GitHub.