calcom/cal.diy · error · Error

Unhandled appSlug: ${appSlug}

Error message

Unhandled appSlug: ${appSlug}

What it means

Thrown by the example credential-sync setTokenInCalCom endpoint when req.query.appSlug is neither 'google-calendar' nor 'zoom'. Unlike getToken, this reads appSlug from query params and pushes a token into Cal.com via the credential-sync admin API. The error message interpolates the offending slug for easier debugging.

Source

Thrown at example-apps/credential-sync/pages/api/setTokenInCalCom.ts:25

  CALCOM_CREDENTIAL_SYNC_SECRET,
  CALCOM_CREDENTIAL_SYNC_HEADER_NAME,
  CALCOM_ADMIN_API_KEY,
} from "../../constants";
import { generateGoogleCalendarAccessToken, generateZoomAccessToken } from "../../lib/integrations";

export default async function handler(req: NextApiRequest, res) {
  const isInvalid = req.query.invalid === "1";
  const userId = parseInt(req.query.userId as string, 10);
  const appSlug = req.query.appSlug;

  try {
    let accessToken;
    if (appSlug === "google-calendar") {
      accessToken = await generateGoogleCalendarAccessToken();
    } else if (appSlug === "zoom") {
      accessToken = await generateZoomAccessToken();
    } else {
      throw new Error(`Unhandled appSlug: ${appSlug}`);
    }

    if (!accessToken) {
      return res.status(500).json({ error: "Could not get access token" });
    }

    const result = await fetch(
      `http://localhost:3002/api/v1/credential-sync?apiKey=${CALCOM_ADMIN_API_KEY}&userId=${userId}`,
      {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          [CALCOM_CREDENTIAL_SYNC_HEADER_NAME]: CALCOM_CREDENTIAL_SYNC_SECRET,
        },
        body: JSON.stringify({
          appSlug,
          encryptedKey: symmetricEncrypt(
            JSON.stringify({

View on GitHub (pinned to 176037d0af)

Solutions

  1. Add a branch (or a lookup table) for the new appSlug and its token generator.
  2. Ensure the request includes appSlug as a query parameter with an exact supported value.
  3. Validate appSlug at the top and return a clear 400 listing supported slugs.

Example fix

// before
if (appSlug === 'google-calendar') { ... }
else if (appSlug === 'zoom') { ... }
else { throw new Error(`Unhandled appSlug: ${appSlug}`); }
// after - validate early with a helpful message
const supported = ['google-calendar', 'zoom'];
if (!supported.includes(appSlug)) {
  return res.status(400).json({ error: `Unsupported appSlug '${appSlug}'. Supported: ${supported.join(', ')}` });
}
Defensive patterns

Strategy: validation

Validate before calling

const supported = ['google-calendar', 'zoom'];
if (!supported.includes(appSlug as string)) {
  return res.status(400).json({ error: `Unsupported appSlug '${appSlug}'. Supported: ${supported.join(', ')}` });
}

Type guard

function isSupportedSlug(s: unknown): s is 'google-calendar' | 'zoom' {
  return s === 'google-calendar' || s === 'zoom';
}

Try / catch

null

Prevention

When it happens

Trigger: GET /api/setTokenInCalCom?appSlug=<other>&userId=... where appSlug is not one of the two supported values, or is missing entirely (undefined).

Common situations: Adding a third provider without extending setTokenInCalCom; the query string missing appSlug; a typo like 'gcal' instead of 'google-calendar'; automated test hitting the endpoint with an unsupported slug.

Related errors


AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12). Data as JSON: /api/errors/a30224cfd9f7baec. Report an issue: GitHub.