calcom/cal.diy · error · HttpError
You must be logged in to do this
Error message
You must be logged in to do this
What it means
Thrown as an HttpError (HTTP 401) by the Dub OAuth callback when the session user has no id. The callback exchanges the Dub authorization code for tokens and must attribute the resulting credential to a Cal.com user; without a session user id, the install cannot be saved.
Source
Thrown at packages/app-store/dub/api/callback.ts:31
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { code } = req.query;
const state = decodeOAuthState(req, "dub");
if (typeof code !== "string") {
if (state?.onErrorReturnTo || state?.returnTo) {
res.redirect(
getSafeRedirectUrl(state.onErrorReturnTo) ??
getSafeRedirectUrl(state?.returnTo) ??
`${WEBAPP_URL}/apps/installed`
);
return;
}
throw new HttpError({ statusCode: 400, message: "`code` must be a string" });
}
if (!req.session?.user?.id) {
throw new HttpError({ statusCode: 401, message: "You must be logged in to do this" });
}
const { client_id, redirect_uris, client_secret } = await getParsedAppKeysFromSlug("dub", dubAppKeysSchema);
const codeExchangeUrl = `https://api.dub.co/oauth/token`;
const result = await fetch(codeExchangeUrl, {
method: "POST",
body: new URLSearchParams({
code,
client_id,
redirect_uri: redirect_uris,
client_secret,
grant_type: "authorization_code",
}).toString(),
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},View on GitHub (pinned to 176037d0af)
Solutions
- Increase session TTL or use a database session strategy so the OAuth round-trip survives.
- Set NEXTAUTH cookie SameSite=None; Secure if the Dub redirect is treated as cross-site.
- Restart the OAuth flow (re-authenticate) when this fires.
- Encode userId in OAuth state so the callback can recover the install context if the session dropped.
Example fix
// before
if (!req.session?.user?.id) {
throw new HttpError({ statusCode: 401, message: "You must be logged in to do this" });
}
// after - redirect to login preserving the install intent
if (!req.session?.user?.id) {
const { code } = req.query;
const loginUrl = `${WEBAPP_URL}/auth/login?callbackUrl=${encodeURIComponent(`${req.url}`)}`;
res.redirect(loginUrl);
return;
} Defensive patterns
Strategy: validation
Validate before calling
const session = await getSession({ req });
if (!session?.user?.id) {
// store the install intent in a cookie/state so it survives re-login
res.redirect(`${WEBAPP_URL}/auth/login?callbackUrl=${encodeURIComponent(req.url ?? "")}`);
return;
} Type guard
const hasSessionUserId = (req: NextApiRequest): boolean => typeof req.session?.user?.id === "number";
Try / catch
if (!req.session?.user?.id) {
res.redirect(`${WEBAPP_URL}/auth/login?callbackUrl=${encodeURIComponent(req.url ?? "")}`);
return;
} Prevention
- Use database-backed sessions so the OAuth round-trip survives cookie expiry.
- Encode userId in the OAuth state to recover context if the session drops.
- Set cookie SameSite appropriately for cross-site OAuth redirects.
When it happens
Trigger: OAuth callback invoked after the NextAuth session expired mid-flow (user started Dub install, left the page, came back hours later, Dub redirected back), or session cookie was cleared between add and callback.
Common situations: Long OAuth round-trip exceeding session TTL; NextAuth cookie blocked; user opened the install in one browser and the callback in another; cookie SameSite=Lax dropping the cookie on the cross-site redirect.
Related errors
- You must be logged in to do this
- `code` must be a string
- Session user must have an email
- `code` must be a string
- Missing `state` query param
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/294c88bd5779ddb3.
Report an issue: GitHub.