calcom/cal.diy · error · NotFoundException

Calendar connection not found

Error message

Calendar connection not found

What it means

Thrown by getCalendarClientByCredentialId when findCredentialByIdAndUserId(credentialId, userId) returns null — the supplied credentialId does not exist OR does not belong to the supplied userId. Returns HTTP 404 via NotFoundException. This is the connection-scoped entry point (listConnectionEvents, createConnectionEvent, etc.).

Source

Thrown at apps/api/v2/src/modules/cal-unified-calendars/services/google-calendar.service.ts:196

    }
    if (credential.invalid) {
      throw new UnauthorizedException("Google Calendar credentials are invalid. Please reconnect.");
    }
    return this.getAuthorizedCalendarInstance(
      credential.user?.email ?? undefined,
      credential.key,
      credential.delegationCredentialId ? { id: credential.delegationCredentialId } : null
    );
  }

  /**
   * Gets an authorized Google Calendar instance for a specific credential (connection).
   * Tries delegated auth first (if available), then falls back to direct OAuth.
   */
  async getCalendarClientByCredentialId(userId: number, credentialId: number): Promise<calendar_v3.Calendar> {
    const credential = await this.credentialsRepository.findCredentialByIdAndUserId(credentialId, userId);
    if (!credential) {
      throw new NotFoundException("Calendar connection not found");
    }
    if (credential.type !== GOOGLE_CALENDAR_TYPE) {
      throw new BadRequestException(
        "Event operations for this connection are currently only available for Google Calendar"
      );
    }
    if (credential.invalid) {
      throw new UnauthorizedException("Calendar credentials are invalid. Please reconnect.");
    }
    return this.getAuthorizedCalendarInstance(
      credential.user?.email ?? undefined,
      credential.key,
      credential.delegationCredentialId ? { id: credential.delegationCredentialId } : null
    );
  }

  // ─── Shared private helpers (DRY calendar CRUD) ──────────────────────

View on GitHub (pinned to 176037d0af)

Solutions

  1. Refresh the connections list via GET /v2/calendars/connections and use a current connectionId.
  2. Confirm the credentialId belongs to the calling user: SELECT id, "userId" FROM "AppCredential" WHERE id=? AND "userId"=?.
  3. If the credential was deleted, instruct the user to reconnect and obtain a new connectionId.
  4. Validate that connectionId is a positive integer before the request.

Example fix

// before
const event = await googleCalendarService.getEventByConnectionId(userId, Number(connectionId), calId, evId);

// after
const connectionIdNum = Number(connectionId);
if (!Number.isInteger(connectionIdNum) || connectionIdNum <= 0) {
  throw new BadRequestException('connectionId must be a positive integer.');
}
const cred = await credentialsRepository.findCredentialByIdAndUserId(connectionIdNum, userId);
if (!cred) {
  throw new NotFoundException(`No calendar connection ${connectionIdNum} for user ${userId}.`);
}
Defensive patterns

Strategy: validation

Validate before calling

function parseConnectionId(raw: string): number {
  const n = Number(raw);
  if (!Number.isInteger(n) || n <= 0) {
    throw new BadRequestException('connectionId must be a positive integer.');
  }
  return n;
}

async function connectionBelongsToUser(connectionId: number, userId: number): Promise<boolean> {
  const cred = await credentialsRepository.findCredentialByIdAndUserId(connectionId, userId);
  return Boolean(cred);
}

const id = parseConnectionId(req.params.connectionId);
if (!(await connectionBelongsToUser(id, userId))) {
  throw new NotFoundException('Calendar connection not found.');
}

Type guard

function isPositiveInt(n: unknown): n is number {
  return typeof n === 'number' && Number.isInteger(n) && n > 0;
}

Try / catch

try {
  return await googleCalendarService.getEventByConnectionId(userId, credentialId, calId, evId);
} catch (e) {
  if (e instanceof NotFoundException && /not found/i.test(e.message)) {
    return res.status(404).json({ code: 'connection_not_found', connectionId });
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling any /v2/calendars/connections/{connectionId}/... endpoint with a connectionId that is stale, deleted, belongs to another user, or was transposed (typo). Also triggered when a team/org credential is referenced by userId when the query filters by owner.

Common situations: Frontend cached an old connection list after the user disconnected; cross-tenant access attempt; integer parsing of connectionId produced a wrong value; the connection was deleted in another session.

Related errors


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