{"record":{"id":"a30224cfd9f7baec","repo":"calcom/cal.diy","slug":"unhandled-appslug-appslug","errorCode":null,"errorMessage":"Unhandled appSlug: ${appSlug}","messagePattern":"Unhandled appSlug: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"example-apps/credential-sync/pages/api/setTokenInCalCom.ts","lineNumber":25,"sourceCode":"  CALCOM_CREDENTIAL_SYNC_SECRET,\n  CALCOM_CREDENTIAL_SYNC_HEADER_NAME,\n  CALCOM_ADMIN_API_KEY,\n} from \"../../constants\";\nimport { generateGoogleCalendarAccessToken, generateZoomAccessToken } from \"../../lib/integrations\";\n\nexport default async function handler(req: NextApiRequest, res) {\n  const isInvalid = req.query.invalid === \"1\";\n  const userId = parseInt(req.query.userId as string, 10);\n  const appSlug = req.query.appSlug;\n\n  try {\n    let accessToken;\n    if (appSlug === \"google-calendar\") {\n      accessToken = await generateGoogleCalendarAccessToken();\n    } else if (appSlug === \"zoom\") {\n      accessToken = await generateZoomAccessToken();\n    } else {\n      throw new Error(`Unhandled appSlug: ${appSlug}`);\n    }\n\n    if (!accessToken) {\n      return res.status(500).json({ error: \"Could not get access token\" });\n    }\n\n    const result = await fetch(\n      `http://localhost:3002/api/v1/credential-sync?apiKey=${CALCOM_ADMIN_API_KEY}&userId=${userId}`,\n      {\n        method: \"POST\",\n        headers: {\n          \"Content-Type\": \"application/json\",\n          [CALCOM_CREDENTIAL_SYNC_HEADER_NAME]: CALCOM_CREDENTIAL_SYNC_SECRET,\n        },\n        body: JSON.stringify({\n          appSlug,\n          encryptedKey: symmetricEncrypt(\n            JSON.stringify({","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/example-apps/credential-sync/pages/api/setTokenInCalCom.ts#L7-L43","documentation":"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.","triggerScenarios":"GET /api/setTokenInCalCom?appSlug=<other>&userId=... where appSlug is not one of the two supported values, or is missing entirely (undefined).","commonSituations":"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.","solutions":["Add a branch (or a lookup table) for the new appSlug and its token generator.","Ensure the request includes appSlug as a query parameter with an exact supported value.","Validate appSlug at the top and return a clear 400 listing supported slugs."],"exampleFix":"// before\nif (appSlug === 'google-calendar') { ... }\nelse if (appSlug === 'zoom') { ... }\nelse { throw new Error(`Unhandled appSlug: ${appSlug}`); }\n// after - validate early with a helpful message\nconst supported = ['google-calendar', 'zoom'];\nif (!supported.includes(appSlug)) {\n  return res.status(400).json({ error: `Unsupported appSlug '${appSlug}'. Supported: ${supported.join(', ')}` });\n}","handlingStrategy":"validation","validationCode":"const supported = ['google-calendar', 'zoom'];\nif (!supported.includes(appSlug as string)) {\n  return res.status(400).json({ error: `Unsupported appSlug '${appSlug}'. Supported: ${supported.join(', ')}` });\n}","typeGuard":"function isSupportedSlug(s: unknown): s is 'google-calendar' | 'zoom' {\n  return s === 'google-calendar' || s === 'zoom';\n}","tryCatchPattern":"null","preventionTips":["Validate appSlug query param against an allowlist at the top of the handler.","Extend the allowlist and branches together when adding providers.","Return supported values in the error to guide the caller."],"tags":["credential-sync","example-app","validation","query-params"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}