calcom/cal.diy · error · NotFoundException

Updated link not found

Error message

Updated link not found

What it means

First of two 'Updated link not found' guards in PrivateLinksService.updatePrivateLink. After repo.update returns, the result is checked: if it is falsy OR its (loosely-cast) `.count` is 0, NotFoundException is thrown: no row matched the (eventTypeId, link) pair, so nothing was updated. This is HTTP 404 because the targeted resource doesn't exist.

Source

Thrown at apps/api/v2/src/platform/event-types-private-links/services/private-links.service.ts:80

      return this.outputService.transformArrayToOutput(mapped);
    } catch (error) {
      if (error instanceof Error) {
        throw new BadRequestException(error.message);
      }
      throw new BadRequestException("Failed to get private links");
    }
  }

  async updatePrivateLink(eventTypeId: number, input: UpdatePrivateLinkInput): Promise<PrivateLinkOutput> {
    try {
      const transformedInput = this.inputService.transformUpdateInput(input);
      const updatedResult = await this.repo.update(eventTypeId, {
        link: transformedInput.linkId,
        expiresAt: transformedInput.expiresAt ?? null,
        maxUsageCount: transformedInput.maxUsageCount ?? null,
      });
      if (!updatedResult || (updatedResult as any).count === 0) {
        throw new NotFoundException("Updated link not found");
      }
      const updated = await this.repo.findWithEventTypeDetails(transformedInput.linkId);
      if (!updated) throw new NotFoundException("Updated link not found");
      const mapped: PrivateLinkData = {
        id: updated.link,
        eventTypeId,
        isExpired: isLinkExpired(updated as any),
        bookingUrl: `${process.env.NEXT_PUBLIC_WEBAPP_URL || "https://cal.com"}/d/${updated.link}`,
        expiresAt: updated.expiresAt ?? null,
        maxUsageCount: updated.maxUsageCount ?? null,
        usageCount: updated.usageCount ?? 0,
      };
      return this.outputService.transformToOutput(mapped);
    } catch (error) {
      if (error instanceof Error) {
        if (error.message.includes("not found")) {
          throw new NotFoundException(error.message);
        }

View on GitHub (pinned to 176037d0af)

Solutions

  1. Verify the linkId exists for the given eventTypeId (GET /v2/event-types/{id}/private-links) before PATCHing.
  2. Refresh the client's link list if it may be stale.
  3. Treat a 404 on update as 'already gone' and refresh the list rather than retrying blindly.
Defensive patterns

Strategy: validation

Validate before calling

const links = await privateLinksService.getPrivateLinks(eventTypeId);
const exists = links.some(l => l.id === linkId);
if (!exists) {
  return { status: 'not_found' }; // refresh the client's list instead of PATCHing
}

Type guard

function isExistingLinkId(links: { id: string }[], linkId: string): boolean {
  return links.some(l => l.id === linkId);
}

Try / catch

try {
  await privateLinksService.updatePrivateLink(eventTypeId, input);
} catch (e) {
  if (e instanceof NotFoundException && e.message === 'Updated link not found') {
    // refresh the link list; the linkId is stale or wrong
  } else throw e;
}

Prevention

When it happens

Trigger: PATCH /v2/event-types/{id}/private-links/{linkId} where linkId doesn't exist under that eventTypeId; the link was already deleted; the linkId is correct but belongs to a different eventTypeId; a typo or truncated linkId in the request.

Common situations: Stale client showing a link that was since deleted; cross-event-type confusion; copy-paste of a linkId with a trailing character stripped; concurrent delete between GET and PATCH.

Related errors


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