Skip to content

fix(caldav): default calendar not written on booking#28934

Open
Harshithk951 wants to merge 2 commits intocalcom:mainfrom
Harshithk951:fix/caldav-default-calendar-booking-clean-v2
Open

fix(caldav): default calendar not written on booking#28934
Harshithk951 wants to merge 2 commits intocalcom:mainfrom
Harshithk951:fix/caldav-default-calendar-booking-clean-v2

Conversation

@Harshithk951
Copy link
Copy Markdown
Contributor

@Harshithk951 Harshithk951 commented Apr 19, 2026

What does this PR do?

When "Add to calendar" is set to "Default" in the CalDAV app, no calendar entry was created on booking even though confirmation emails were sent correctly.

Root cause: the filter in BaseCalendarService.createEvent fell through with : true when no destinationCalendar externalId was set, causing createCalendarObject to fire against every discovered calendar collection. Servers like GMX silently drop these writes so no entry is ever created.

Fix: replace : true with calendars.slice(0, 1) — use only the first (primary) calendar when no explicit destination is configured. The explicit-calendar path is unchanged.

Mandatory Tasks

  • I have self-reviewed the code (A decent size PR without self-review might be rejected).
  • I have updated the developer docs in /docs if this PR makes changes that would require a documentation change. N/A — no API or config changes.
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

How should this be tested?

  1. Set up CalDAV app with a GMX or any CalDAV provider
  2. Create an event type with "Add to calendar" set to "Default"
  3. Book an appointment
  4. Verify a calendar entry is created in the provider's calendar

Checklist

  • My changes generate no new warnings (Biome + tsc verified)
  • PR is under 500 lines / 10 files
Screen.Recording.2026-03-25.at.8.18.49.PM.mov

Open with Devin

Supersedes: #28934

@github-actions
Copy link
Copy Markdown
Contributor

Welcome to Cal.diy, @Harshithk951! Thanks for opening this pull request.

A few things to keep in mind:

  • This is Cal.diy, not Cal.com. Cal.diy is a community-driven, fully open-source fork of Cal.com licensed under MIT. Your changes here will be part of Cal.diy — they will not be deployed to the Cal.com production app.
  • Please review our Contributing Guidelines if you haven't already.
  • Make sure your PR title follows the Conventional Commits format.

A maintainer will review your PR soon. Thanks for contributing!

@Harshithk951
Copy link
Copy Markdown
Contributor Author

Hey @sahitya-chandra — this is the rebased version of #28445, now with a clean single commit on top of latest main. CI is green. Would appreciate a review when you get a chance!

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 19, 2026

📝 Walkthrough

Walkthrough

The event creation logic in CalendarService has been refactored to compute a target subset of calendars upfront rather than filtering all calendars during processing. The target calendars are determined based on mainHostDestinationCalendar.externalId, or by selecting the first available calendar if no externalId exists. If no target calendars are found, an error is thrown. The Promise.all operation subsequently processes only the computed target calendars, replacing the previous inline filtering behavior.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main fix: addressing the issue where default calendar entries were not being created during CalDAV bookings.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed The PR description clearly explains the issue, root cause, and fix with relevant context about CalDAV calendar booking behavior.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/lib/CalendarService.ts`:
- Around line 466-472: Replace the thrown plain Error with an ErrorWithCode
(e.g., new ErrorWithCode("No target calendars found to create CalDAV calendar
entry", "CALDAV:no-target-calendar")) where the current check on targetCalendars
fails; when selecting the primary calendar prefer the
CALDAV:schedule-default-calendar-URL property if exposed by tsdav (inspect the
collection metadata returned for a schedule-default-calendar-URL and match that
URL against calendars to build targetCalendars) and only if that property is not
available fall back to picking the first calendar from calendars but emit a
debug/info log that includes the chosen calendar URL (log the selected
calendar's href/externalId) so misrouted writes are traceable; update the logic
around mainHostDestinationCalendar, calendars and targetCalendars to implement
these checks and the ErrorWithCode replacement.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5300df62-f419-4d6d-8cc5-a5d87be1f320

📥 Commits

Reviewing files that changed from the base of the PR and between 9efd0e6 and 3eda987.

📒 Files selected for processing (1)
  • packages/lib/CalendarService.ts

Comment on lines +466 to +472
const targetCalendars = mainHostDestinationCalendar?.externalId
? calendars.filter((c) => c.externalId === mainHostDestinationCalendar.externalId)
: calendars.slice(0, 1);

if (targetCalendars.length === 0) {
throw new Error("No target calendars found to create CalDAV calendar entry");
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Does the CalDAV/WebDAV PROPFIND response for calendar-home-set guarantee the primary/default calendar is listed first? Is there a standard property (e.g., CALDAV:schedule-default-calendar-URL) to identify the default calendar?

💡 Result:

No, the CalDAV/WebDAV PROPFIND response for calendar-home-set does not guarantee that the primary/default calendar is listed first. RFC 4791 defines CALDAV:calendar-home-set as containing zero or more DAV:href elements identifying collections with the principal's calendars, with no specification on order (RFC 4791 Section 6.2.1; sources 1,11). Implementations may vary in ordering, and clients cannot rely on position (e.g., GitHub issue on Stalwart notes clients like Apple do not consistently pick first; source 5). Yes, there is a standard property: CALDAV:schedule-default-calendar-URL (urn:ietf:params:xml:ns:caldav), defined in RFC 6638 Section 9.2. It MAY be defined on the scheduling Inbox collection (discovered via CALDAV:schedule-inbox-URL on the principal) and, if present with a DAV:href, points to the default calendar where new scheduling objects are created (sources 6,7,9,10). Servers SHOULD use this for new scheduling objects (RFC 6638 Section 4.3). Some implementations expose it on the principal too, but standard location is the inbox (source 9).

Citations:


🏁 Script executed:

# Check lines around 471 in CalendarService.ts to verify the Error handling
head -n 480 packages/lib/CalendarService.ts | tail -n 30

Repository: calcom/cal.diy

Length of output: 1332


🏁 Script executed:

# Check imports and error handling patterns in CalendarService.ts
head -n 50 packages/lib/CalendarService.ts | grep -E "^import|ErrorWithCode|TRPCError"

Repository: calcom/cal.diy

Length of output: 664


🏁 Script executed:

# Search for ErrorWithCode usage in the codebase to confirm the pattern
rg "ErrorWithCode" --type ts --type tsx -A 2 -B 2 | head -n 30

Repository: calcom/cal.diy

Length of output: 85


🏁 Script executed:

# Search for ErrorWithCode import pattern and usage
rg "ErrorWithCode" --type ts -A 2 -B 2 | head -n 50

Repository: calcom/cal.diy

Length of output: 3255


🏁 Script executed:

# Check if ErrorWithCode is used in lib files (non-tRPC context)
rg "ErrorWithCode" packages/lib --type ts

Repository: calcom/cal.diy

Length of output: 2295


🏁 Script executed:

# Verify if CalendarService.ts uses ErrorWithCode anywhere
grep -n "ErrorWithCode\|import.*Error" packages/lib/CalendarService.ts

Repository: calcom/cal.diy

Length of output: 40


🏁 Script executed:

# Check how other errors are thrown in CalendarService.ts
rg "throw new Error|throw.*Error" packages/lib/CalendarService.ts | head -n 20

Repository: calcom/cal.diy

Length of output: 261


Use ErrorWithCode instead of new Error, and prefer the standard CALDAV:schedule-default-calendar-URL property for identifying the primary calendar.

The fix correctly avoids writing to all discovered collections, but has two issues:

  1. Error type: Line 471 throws new Error(...), violating the guideline to use ErrorWithCode in non-tRPC lib files. Refactor to ErrorWithCode with an appropriate error code.

  2. Calendar selection fallback: calendars.slice(0, 1) assumes the server returns the primary calendar first, but RFC 4791 (CalDAV) does not guarantee PROPFIND response order. Per RFC 6638 Section 9.2, servers expose the default calendar via the CALDAV:schedule-default-calendar-URL property (typically on the scheduling inbox collection discovered via CALDAV:schedule-inbox-URL). If tsdav surfaces this property, prefer it over positional fallback. If not available, at least log which calendar URL was chosen when falling back to the first one, so misrouted writes are debuggable.

Note: The behavior change is intentional — previously an unmatched externalId produced an empty list and Promise.all([]) resolved silently; now it throws and triggers the compensating-update pattern (per RegularBookingService), potentially flipping a booking to PENDING. More correct, but user-visible.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/lib/CalendarService.ts` around lines 466 - 472, Replace the thrown
plain Error with an ErrorWithCode (e.g., new ErrorWithCode("No target calendars
found to create CalDAV calendar entry", "CALDAV:no-target-calendar")) where the
current check on targetCalendars fails; when selecting the primary calendar
prefer the CALDAV:schedule-default-calendar-URL property if exposed by tsdav
(inspect the collection metadata returned for a schedule-default-calendar-URL
and match that URL against calendars to build targetCalendars) and only if that
property is not available fall back to picking the first calendar from calendars
but emit a debug/info log that includes the chosen calendar URL (log the
selected calendar's href/externalId) so misrouted writes are traceable; update
the logic around mainHostDestinationCalendar, calendars and targetCalendars to
implement these checks and the ErrorWithCode replacement.

@Harshithk951
Copy link
Copy Markdown
Contributor Author

Superseded by #28943 which includes the CodeRabbit fixes.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant