calcom/cal.diy · error · HttpError
`code` must be a string
Error message
`code` must be a string
What it means
Thrown as an HttpError (HTTP 400) by the Dub OAuth callback when the `code` query parameter is not a string. OAuth callbacks receive code as a single string; a non-string (undefined, or string[] when duplicated) means the callback was invoked without an authorization code (user denied consent, or a malformed redirect). The handler only throws if no safe returnTo/onErrorReturnTo redirect target exists.
Source
Thrown at packages/app-store/dub/api/callback.ts:27
import createOAuthAppCredential from "../../_utils/oauth/createOAuthAppCredential";
import { decodeOAuthState } from "../../_utils/oauth/decodeOAuthState";
import { dubAppKeysSchema } from "../lib/utils";
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",View on GitHub (pinned to 176037d0af)
Solutions
- Always encode a returnTo / onErrorReturnTo in the OAuth state so denial degrades to a redirect, not a thrown 400.
- Handle the OAuth `error` and `error_description` query params (e.g. access_denied) explicitly before checking code.
- If code is an array, take the first element: const code = Array.isArray(req.query.code) ? req.query.code[0] : req.query.code.
- Confirm the Dub app's redirect_uris matches the Cal.com callback URL exactly.
Example fix
// before
if (typeof code !== "string") {
if (state?.onErrorReturnTo || state?.returnTo) { res.redirect(...); return; }
throw new HttpError({ statusCode: 400, message: "`code` must be a string" });
}
// after - treat denial / array gracefully
const code = Array.isArray(req.query.code) ? req.query.code[0] : req.query.code;
if (typeof code !== "string") {
if (req.query.error) {
res.redirect(`${WEBAPP_URL}/apps/installed?error=${encodeURIComponent(req.query.error)}`);
return;
}
res.redirect(`${WEBAPP_URL}/apps/installed`);
return;
} Defensive patterns
Strategy: validation
Validate before calling
const rawCode = Array.isArray(req.query.code) ? req.query.code[0] : req.query.code;
if (typeof rawCode !== "string") {
if (req.query.error) {
res.redirect(`${WEBAPP_URL}/apps/installed?error=${encodeURIComponent(String(req.query.error))}`);
return;
}
res.redirect(`${WEBAPP_URL}/apps/installed`);
return;
} Type guard
const isStringCode = (v: unknown): v is string => typeof v === "string" && v.length > 0;
Try / catch
if (typeof code !== "string") {
const fallback = getSafeRedirectUrl(state?.onErrorReturnTo) ?? getSafeRedirectUrl(state?.returnTo) ?? `${WEBAPP_URL}/apps/installed`;
res.redirect(fallback);
return;
} Prevention
- Always encode onErrorReturnTo / returnTo in the OAuth state.
- Handle the OAuth error/error_description query params explicitly.
- Normalize array query params before the type check.
When it happens
Trigger: Dub redirects back to /api/dub/callback with no `code` (user clicked 'Deny' on the Dub consent screen), with code as an array (?code=a&code=b), or with an error param instead of code; AND state.onErroredReturnTo / state.returnTo are also unset.
Common situations: User cancels the Dub OAuth consent; Dub misconfigured redirect_uri sending extra query params; integrations page opened the OAuth flow without setting a returnTo state.
Related errors
- `code` must be a string
- Session user must have an email
- You must be logged in to do this
- Missing `state` query param
- {error_description}
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/7ed595e475e891c1.
Report an issue: GitHub.