calcom/cal.diy · error · Error

Unable to generate token

Error message

Unable to generate token

What it means

Thrown by the example credential-sync getToken endpoint when the chosen token-generation function (generateGoogleCalendarAccessToken or generateZoomAccessToken) returned a falsy value. This indicates the provider-specific generation logic ran but produced no usable token. Caught and returned as a 500 response.

Source

Thrown at example-apps/credential-sync/pages/api/getToken.ts:32

      return res.status(403).json({ message: "Invalid secret" });
    }

    const calcomUserId = req.body.calcomUserId;
    const appSlug = req.body.appSlug;
    console.log("getToken Params", {
      calcomUserId,
      appSlug,
    });
    let accessToken;
    if (appSlug === "google-calendar") {
      accessToken = await generateGoogleCalendarAccessToken();
    } else if (appSlug === "zoom") {
      accessToken = await generateZoomAccessToken();
    } else {
      throw new Error("Unhandled values");
    }
    if (!accessToken) {
      throw new Error("Unable to generate token");
    }
    res.status(200).json({
      _1: true,
      access_token: accessToken,
    });
  } catch (e) {
    res.status(500).json({ error: e.message });
  }
}

View on GitHub (pinned to 176037d0af)

Solutions

  1. Set the provider credentials required by lib/integrations (e.g. Google service account, Zoom client secret/refresh token) in the example app environment.
  2. Add logging inside the generator to see why it returned empty (provider error, expired refresh token).
  3. Re-authorize the provider to obtain a fresh refresh token.
  4. Return a non-empty token from the generator or throw a descriptive error instead of returning undefined.

Example fix

// before - generator returns undefined silently
export async function generateZoomAccessToken() {
  const r = await fetch(...);
  return r.ok ? (await r.json()).access_token : undefined;
}
// after - throw with reason so getToken can surface it
export async function generateZoomAccessToken() {
  const r = await fetch(...);
  if (!r.ok) throw new Error(`Zoom token HTTP ${r.status}`);
  return (await r.json()).access_token;
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (!accessToken) {
  // surface a clear reason instead of a generic throw
  return res.status(502).json({ error: `Token generation returned empty for ${appSlug}` });
}

Type guard

function isNonEmptyToken(t: unknown): t is string {
  return typeof t === 'string' && t.length > 0;
}

Try / catch

try {
  accessToken = appSlug === 'google-calendar' ? await generateGoogleCalendarAccessToken() : await generateZoomAccessToken();
} catch (genError) {
  return res.status(502).json({ error: `Generator failed: ${genError.message}` });
}
if (!accessToken) return res.status(502).json({ error: 'Empty token from provider' });

Prevention

When it happens

Trigger: POST /api/getToken with a supported appSlug, but the underlying generator returned null/undefined/empty string. Typically because the example app's service-account credentials, refresh tokens, or OAuth client secrets are missing or invalid inside lib/integrations.

Common situations: Example app env vars (Google service account JSON, Zoom OAuth secrets) not set; refresh token expired or revoked; the generator silently returns undefined on a caught internal error; clock skew or quota limits causing the provider to return an empty token.

Related errors


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