Skip to content

Fixes that workflow reminders of cancelled and rescheduled bookings are still sent#1

Open
sartorijr92 wants to merge 8 commits intobase/pr-7232from
head/pr-7232
Open

Fixes that workflow reminders of cancelled and rescheduled bookings are still sent#1
sartorijr92 wants to merge 8 commits intobase/pr-7232from
head/pr-7232

Conversation

@sartorijr92
Copy link
Copy Markdown

What does this PR do?

Fixes that workflow emails are still sent for cancelled and rescheduled bookings. The fix of PR calcom#6991 was wrong and didn't work as expected.

The SendGrid endpoint DELETE /v3/user/scheduled_sends/${referenceId} was not used correctly. This endpoint deletes a cancelled scheduled email from scheduled_sends by removing the 'cancel' status (so email will again be scheduled).

The reason why cancelling our scheduled emails stopped working is that all cancerlled emails are saved in scheduled_sends until the scheduled date but the max. of pending cancellations is 100. Once we reached 100 pending cancellations, new cancellations failed and emails were still sent out.

The solution implemented in this PR:

  • A new field cancelled is added to the workflowReminder model
  • Once an already scheduled email is cancelled, the field is set to true (nothing else is done at this point in time)
  • The CRON job scheduleEmailReminders is responsible for the final cancellation of the scheduled email (runs every 15 mins)
    • cancels all scheduled emails that are cancelled and that have their scheduled date within the next hour

Fixes calcom#7225

Environment: Staging(main branch) / Production

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Chore (refactoring code, technical debt, workflow improvements)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

@sartorijr92
Copy link
Copy Markdown
Author

@kody start-review

@sartorijr92
Copy link
Copy Markdown
Author

sartorijr92 commented Apr 29, 2026

Kody Review Complete

Great news! 🎉
No issues were found that match your current review configurations.

Keep up the excellent work! 🚀

Kody Guide: Usage and Configuration
Interacting with Kody
  • Request a Review: Ask Kody to review your PR manually by adding a comment with the @kody start-review command at the root of your PR.

  • Validate Business Logic: Ask Kody to validate your code against business rules by adding a comment with the @kody -v business-logic command.

  • Provide Feedback: Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

Current Kody Configuration
Review Options

The following review options are enabled or disabled:

Options Enabled
Bug
Performance
Security
Business Logic

Access your configuration settings here.

@sartorijr92
Copy link
Copy Markdown
Author

@kody review

@sartorijr92
Copy link
Copy Markdown
Author

sartorijr92 commented Apr 29, 2026

Code Review Completed! 🔥

The code review was successfully completed based on your current configurations.

Kody Guide: Usage and Configuration
Interacting with Kody
  • Request a Review: Ask Kody to review your PR manually by adding a comment with the @kody start-review command at the root of your PR.

  • Validate Business Logic: Ask Kody to validate your code against business rules by adding a comment with the @kody -v business-logic command.

  • Provide Feedback: Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

Current Kody Configuration
Review Options

The following review options are enabled or disabled:

Options Enabled
Bug
Performance
Security
Business Logic

Access your configuration settings here.

},
});

try {
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kody code-review Bug high

Premature loop termination on client.request failure causes PrismaPromise deletions to be silently dropped and blocks subsequent reminder processing. Wrap each iteration in a try/catch and await prisma.workflowReminder.delete inline to ensure individual referenceId errors do not abort the batch.

for (const reminder of remindersToCancel) {
  try {
    await client.request({
      url: "/v3/user/scheduled_sends",
      method: "POST",
      body: {
        batch_id: reminder.referenceId,
        status: "cancel",
      },
    });

    await prisma.workflowReminder.delete({
      where: {
        id: reminder.id,
      },
    });
  } catch (error) {
    console.log(`Error cancelling scheduled Email for reminder ${reminder.id}: ${error}`);
  }
}
Prompt for LLM

File packages/features/ee/workflows/api/scheduleEmailReminders.ts:

Line 53:

WHAT: A single failing `client.request` inside the for-loop jumps to the catch block, leaving `await Promise.all(workflowRemindersToDelete)` unreachable; because Prisma's `prisma.workflowReminder.delete(...)` returns a lazy PrismaPromise that only executes when awaited, every delete collected for already-cancelled reminders is silently dropped. WHY: cancelled reminders stay in the DB with `cancelled: true`, and any reminder positioned after the failing one is never processed this run — if a particular referenceId consistently fails (stale or invalid SendGrid batch ID), all subsequent cancellations remain stuck forever and the emails they were meant to cancel will be sent. HOW: wrap each iteration in its own try/catch so one bad reminder cannot abort the batch, and only delete the DB row after its SendGrid cancel succeeded (await the prisma delete inline rather than relying on a deferred Promise.all that the catch can skip).

Suggested Code:

  for (const reminder of remindersToCancel) {
    try {
      await client.request({
        url: "/v3/user/scheduled_sends",
        method: "POST",
        body: {
          batch_id: reminder.referenceId,
          status: "cancel",
        },
      });

      await prisma.workflowReminder.delete({
        where: {
          id: reminder.id,
        },
      });
    } catch (error) {
      console.log(`Error cancelling scheduled Email for reminder ${reminder.id}: ${error}`);
    }
  }

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant