calcom/cal.diy · critical · Error
Feishu Calendar refresh token expired
Error message
Feishu Calendar refresh token expired
What it means
Thrown by FeishuCalendarService.refreshAccessToken when the stored refresh_token has expired or is missing. Refresh tokens in Feishu have a refresh_expires_date; if that date has passed or the token field is absent, the credential is unrecoverable so the handler DELETES the credential from the DB and throws, forcing the user to re-authenticate.
Source
Thrown at packages/app-store/feishucalendar/lib/CalendarService.ts:65
}
private feishuAuth = (credential: CredentialPayload) => {
const feishuAuthCredentials = credential.key as FeishuAuthCredentials;
return {
getToken: () =>
!isExpired(feishuAuthCredentials.expiry_date)
? Promise.resolve(feishuAuthCredentials.access_token)
: this.refreshAccessToken(credential),
};
};
private refreshAccessToken = async (credential: CredentialPayload) => {
const feishuAuthCredentials = credential.key as FeishuAuthCredentials;
const refreshExpireDate = feishuAuthCredentials.refresh_expires_date;
const refreshToken = feishuAuthCredentials.refresh_token;
if (isExpired(refreshExpireDate) || !refreshToken) {
await prisma.credential.delete({ where: { id: credential.id } });
throw new Error("Feishu Calendar refresh token expired");
}
try {
const appAccessToken = await getAppAccessToken();
const resp = await refreshOAuthTokens(
async () =>
await fetch(`${this.url}/authen/v1/refresh_access_token`, {
method: "POST",
headers: {
Authorization: `Bearer ${appAccessToken}`,
"Content-Type": "application/json; charset=utf-8",
},
body: JSON.stringify({
grant_type: "refresh_token",
refresh_token: refreshToken,
}),
}),
"feishu-calendar",
credential.userIdView on GitHub (pinned to 176037d0af)
Solutions
- Re-authenticate the Feishu calendar integration: the credential has already been deleted, so the user must reinstall it from /apps/feishucalendar.
- Verify isExpired() expects the same time unit (seconds since epoch) as refresh_expires_date to avoid false positives.
- Schedule a periodic token refresh before refresh_expires_date to keep the credential alive.
- Confirm the OAuth install flow stores refresh_token and refresh_expires_date correctly.
Example fix
// before - hard delete + throw
if (isExpired(refreshExpireDate) || !refreshToken) {
await prisma.credential.delete({ where: { id: credential.id } });
throw new Error("Feishu Calendar refresh token expired");
}
// after - mark invalid instead of deleting, so audit trail remains
if (isExpired(refreshExpireDate) || !refreshToken) {
await prisma.credential.update({ where: { id: credential.id }, data: { invalid: true } });
throw new Error("Feishu Calendar refresh token expired; please reconnect the integration.");
} Defensive patterns
Strategy: try-catch
Validate before calling
const cred = await prisma.credential.findUnique({ where: { id: credentialId } });
const key = cred?.key as FeishuAuthCredentials | undefined;
if (!key?.refresh_token || isExpired(key.refresh_expires_date)) {
throw new Error("Feishu refresh token expired or missing; user must reconnect.");
} Type guard
const hasValidFeishuRefresh = (k: unknown): k is FeishuAuthCredentials => typeof k === "object" && k !== null && typeof (k as any).refresh_token === "string" && (k as any).refresh_token.length > 0 && !isExpired((k as any).refresh_expires_date);
Try / catch
try {
await calendarService.createEvent(event, credentialId);
} catch (err) {
if (err instanceof Error && /refresh token expired/i.test(err.message)) {
// credential was deleted; prompt reconnect
await notifyUserReconnectFeishu(userId);
}
throw err;
} Prevention
- Background-refresh Feishu tokens well before refresh_expires_date.
- Verify refresh_expires_date is stored in the same unit isExpired() expects.
- Notify users proactively when their Feishu connection nears refresh-token expiry.
When it happens
Trigger: Calling any calendar operation that needs a fresh access token after refresh_expires_date has elapsed, or when feishuAuthCredentials.refresh_token was never stored (incomplete OAuth install). The credential row is removed as a side effect.
Common situations: User has not used Feishu calendar for > 30 days (refresh token expired); OAuth install was interrupted before refresh_token persisted; refresh_expires_date stored in wrong unit (seconds vs ms) causing premature expiry.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- No app ticket found
- get access token error
- Invalid refresh token
- No credential found for user
- No credential found for user
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/9ba7cfd6f80d1e9f.
Report an issue: GitHub.