calcom/cal.diy · critical · InternalServerErrorException
Could not set ${app} as default conferencing app
Error message
Could not set ${app} as default conferencing app What it means
Thrown by ConferencingService.setDefaultConferencingApp as an InternalServerErrorException (HTTP 500) when usersRepository.setDefaultConferencingApp returns a user whose metadata.defaultConferencingApp.appSlug does not match the requested app. This is a post-write verification that catches DB write failures, race conditions, or metadata-shape regressions. Unlike the 400s in this file, this indicates a server-side fault, not a client error.
Source
Thrown at apps/api/v2/src/modules/conferencing/services/conferencing.service.ts:123
async disconnectConferencingApp(user: UserWithProfile, app: string) {
const credential = await this.checkAppIsValidAndConnected(user, app);
return handleDeleteCredential({
userId: user.id,
userMetadata: user?.metadata,
credentialId: credential.id,
});
}
async setDefaultConferencingApp(user: UserWithProfile, app: string) {
// cal-video is global, so we can skip this check
if (app !== CAL_VIDEO) {
await this.checkAppIsValidAndConnected(user, app);
}
const updatedUser = await this.usersRepository.setDefaultConferencingApp(user.id, app);
const metadata = updatedUser.metadata as { defaultConferencingApp?: { appSlug?: string } };
if (metadata?.defaultConferencingApp?.appSlug !== app) {
throw new InternalServerErrorException(`Could not set ${app} as default conferencing app`);
}
return true;
}
async generateOAuthUrl(app: string, state: OAuthCallbackState) {
switch (app) {
case ZOOM:
return await this.zoomVideoService.generateZoomAuthUrl(JSON.stringify(state));
case OFFICE_365_VIDEO:
return await this.office365VideoService.generateOffice365AuthUrl(JSON.stringify(state));
default:
throw new BadRequestException(
"Invalid conferencing app, available apps are: ",
[ZOOM, OFFICE_365_VIDEO].join(", ")
);
}View on GitHub (pinned to 176037d0af)
Solutions
- Retry the request once — if it was a race, the second call usually verifies cleanly.
- Inspect the user metadata row after the failure: SELECT metadata->'defaultConferencingApp' FROM users WHERE id=? — confirm the actual shape.
- Ensure no concurrent setDefaultConferencingApp calls (client-side lock or queue).
- If reproducible, audit usersRepository.setDefaultConferencingApp for the exact write logic and any metadata parse/stringify that drops fields.
Example fix
// before: service reads back and compares
const updatedUser = await this.usersRepository.setDefaultConferencingApp(user.id, app);
if (metadata?.defaultConferencingApp?.appSlug !== app) {
throw new InternalServerErrorException(`Could not set ${app} as default conferencing app`);
}
// after: log the mismatch shape to aid diagnosis
if (metadata?.defaultConferencingApp?.appSlug !== app) {
this.logger.error('defaultConferencingApp write did not verify', { userId: user.id, requested: app, actual: metadata });
throw new InternalServerErrorException(`Could not set ${app} as default conferencing app`, { cause: { actual: metadata } });
} Defensive patterns
Strategy: retry
Validate before calling
// Pre-flight: confirm no concurrent default-app write is in progress (advisory lock).
async function acquireDefaultAppLock(userId: number): Promise<boolean> {
// pseudo: SETNX lock key in Redis
return redis.set(`default-app-lock:${userId}`, '1', 'NX', 'EX', 5) === 'OK';
}
if (!(await acquireDefaultAppLock(userId))) {
throw new Error('Another default-app update is in progress. Retry shortly.');
} Type guard
function defaultAppWriteVerified(updated: { metadata?: { defaultConferencingApp?: { appSlug?: string } } }, expected: string): boolean {
return updated?.metadata?.defaultConferencingApp?.appSlug === expected;
} Try / catch
async function setDefaultWithRetry(user: UserWithProfile, app: string, attempts = 2): Promise<void> {
try {
await conferencingService.setDefaultConferencingApp(user, app);
} catch (e) {
if (e instanceof InternalServerErrorException && attempts > 0) {
await new Promise(r => setTimeout(r, 200));
return setDefaultWithRetry(user, app, attempts - 1);
}
throw e;
}
} Prevention
- Disable the default-app control client-side for the duration of the in-flight request.
- Audit usersRepository.setDefaultConferencingApp for metadata parse/stringify round-trips that could drop fields.
- Add a distributed lock around concurrent default-app writes per user.
- Log the post-write metadata shape on mismatch to accelerate root-cause.
When it happens
Trigger: A concurrent setDefaultConferencingApp call overwrote metadata between the write and the read-back; userMetadata serialization strips the appSlug; a DB trigger or Prisma middleware mutates metadata; the upsert partially failed.
Common situations: Two browser tabs setting default apps simultaneously; metadata schema drift after an upgrade; a custom Prisma extension modifying metadata on update; transient DB error masked by a non-throwing partial write.
Related errors
- Invalid app, available apps are:
- Booking with uid ${uid} not found
- Event type with uid ${uid} not found
- No users found or no team present for event type with uid ${
- Missing `state` query param
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/f3e5fc249317ab78.
Report an issue: GitHub.