calcom/cal.diy · critical · BadRequestException

Failed to delete private link

Error message

Failed to delete private link

What it means

Fallback BadRequestException thrown by PrivateLinksService.deletePrivateLink when the caught value is not an Error instance. This defensive branch covers non-Error throws (strings, plain objects) from anywhere in the delete pipeline. The generic message hides the original cause, making it a debugging dead-end without instrumentation.

Source

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

      }
      throw new BadRequestException("Failed to update private link");
    }
  }

  async deletePrivateLink(eventTypeId: number, linkId: string): Promise<void> {
    try {
      const { count } = await this.repo.delete(eventTypeId, linkId);
      if (count === 0) {
        throw new NotFoundException("Deleted link not found");
      }
    } catch (error) {
      if (error instanceof Error) {
        if (error.message.includes("not found")) {
          throw new NotFoundException(error.message);
        }
        throw new BadRequestException(error.message);
      }
      throw new BadRequestException("Failed to delete private link");
    }
  }
}

View on GitHub (pinned to 176037d0af)

Solutions

  1. Search the codebase for `throw` of non-Error values and convert them to `throw new Error(...)`.
  2. Add structured logging in the catch block to capture the raw value and its type before the fallback fires.
  3. Wrap third-party calls so they always reject with Error instances.
  4. Consider this a defect to file against the service: a 400 with 'Failed to delete private link' is not actionable for a client.

Example fix

// before (downstream)
if (!conn) throw 'no db connection';
// after
if (!conn) throw new Error('no db connection');
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await api.delete(url);
} catch (e) {
  if (e.response?.status === 400 && e.response?.data?.message === 'Failed to delete private link') {
    // opaque server defect; retry once then escalate
  } else throw e;
}

Prevention

When it happens

Trigger: A downstream method (repo.delete or Prisma internals) throwing a non-Error primitive; a Promise rejection with a raw value; an older dependency that throws strings; an internal assertion that rejects with a plain object.

Common situations: Custom repository code that does `throw 'delete failed'`; a mocked test double that rejects with a string; a transitive dependency regression after `yarn upgrade`. Because the message is generic, users cannot tell whether the link existed.

Related errors


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