Federated calendar sync deletion crashes in CalDavBackend with CachedCalendarObjectDeletedEvent TypeError
Nobody has claimed this yet.
- Dominant language
- PHP
- Stars
- 36.9k
- Forks
- 5.2k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 713
Description
Steps to reproduce
- Run Nextcloud 33.0.5 with at least one federated calendar in
oc_calendars_federated. - Let
OCA\DAV\BackgroundJob\FederatedCalendarPeriodicSyncJobsync a remote calendar where the sync report contains deleted objects. - The job calls
FederatedCalendarSyncService::syncOne(), which calls:
$this->backend->deleteCalendarObject(
$calendar->getId(),
$objectUri,
CalDavBackend::CALENDAR_TYPE_FEDERATED,
true
);
Expected behaviour
Federated-calendar object deletion should complete without trying to look up a calendar subscription.
Actual behaviour
The background job fails with a TypeError because CalDavBackend::deleteCalendarObject() treats every non-CALENDAR_TYPE_CALENDAR object as a calendar subscription deletion:
if ($calendarType === self::CALENDAR_TYPE_CALENDAR) {
...
} else {
$subscriptionRow = $this->getSubscriptionById($calendarId);
$this->dispatcher->dispatchTyped(new CachedCalendarObjectDeletedEvent($calendarId, $subscriptionRow, [], $data));
}
For CALENDAR_TYPE_FEDERATED (2), getSubscriptionById($calendarId) returns null, and CachedCalendarObjectDeletedEvent requires an array for $subscriptionData.
Sanitized stack trace excerpt:
Error while running background job OCA\DAV\BackgroundJob\FederatedCalendarPeriodicSyncJob
TypeError: OCA\DAV\Events\CachedCalendarObjectDeletedEvent::__construct():
Argument #2 ($subscriptionData) must be of type array, null given,
called in apps/dav/lib/CalDAV/CalDavBackend.php on line 1588
#0 apps/dav/lib/CalDAV/CalDavBackend.php(1588): OCA\DAV\Events\CachedCalendarObjectDeletedEvent->__construct()
#1 lib/public/AppFramework/Db/TTransactional.php(45): OCA\DAV\CalDAV\CalDavBackend->{closure:...}()
#2 apps/dav/lib/CalDAV/CalDavBackend.php(1564): OCA\DAV\CalDAV\CalDavBackend->atomic()
#3 apps/dav/lib/CalDAV/Federation/FederatedCalendarSyncService.php(122): OCA\DAV\CalDAV\CalDavBackend->deleteCalendarObject()
#4 apps/dav/lib/BackgroundJob/FederatedCalendarPeriodicSyncJob.php(47): OCA\DAV\CalDAV\Federation\FederatedCalendarSyncService->syncOne()
#5 lib/public/BackgroundJob/Job.php(50): OCA\DAV\BackgroundJob\FederatedCalendarPeriodicSyncJob->run()
#6 lib/public/BackgroundJob/TimedJob.php(85): OCP\BackgroundJob\Job->start()
#7 core/Service/CronService.php(176): OCP\BackgroundJob\TimedJob->start()
#8 core/Service/CronService.php(98): OC\Core\Service\CronService->runCli()
#9 cron.php(52): OC\Core\Service\CronService->run()
Diagnosis
This does not appear to be orphaned subscription data in the database. On the affected instance:
calendarobjects grouped by calendartype:
calendartype=0 count=564
calendartype=2 count=1210
orphan cached subscription objects:
calendartype=1 objects without calendarsubscriptions row = 0
federated calendars:
2 rows in oc_calendars_federated
The error path is therefore the federated delete branch being routed into the cached-subscription event branch.
createCalendarObject() and updateCalendarObject() already distinguish the three cases:
if ($calendarType === self::CALENDAR_TYPE_CALENDAR) {
...
} elseif ($calendarType === self::CALENDAR_TYPE_SUBSCRIPTION) {
...
} elseif ($calendarType === self::CALENDAR_TYPE_FEDERATED) {
// TODO: implement custom event for federated calendars
}
deleteCalendarObject() appears to need the same structure.
Suggested fix
Change the delete branch from else to elseif ($calendarType === self::CALENDAR_TYPE_SUBSCRIPTION), and add an explicit no-op CALENDAR_TYPE_FEDERATED branch until there is a dedicated federated-calendar deleted event:
if ($calendarType === self::CALENDAR_TYPE_CALENDAR) {
$calendarRow = $this->getCalendarById($calendarId);
$shares = $this->getShares($calendarId);
$this->dispatcher->dispatchTyped(new CalendarObjectDeletedEvent($calendarId, $calendarRow, $shares, $data));
} elseif ($calendarType === self::CALENDAR_TYPE_SUBSCRIPTION) {
$subscriptionRow = $this->getSubscriptionById($calendarId);
$this->dispatcher->dispatchTyped(new CachedCalendarObjectDeletedEvent($calendarId, $subscriptionRow, [], $data));
} elseif ($calendarType === self::CALENDAR_TYPE_FEDERATED) {
// TODO: implement custom event for federated calendars
}
I tested that local change on the affected instance by forcing the previously failing job with:
occ background-job:execute --force-execute <job-id>
After the local patch, the job completed, the federated sync token advanced, and cached federated objects dropped from 1210 to 1200, indicating that the remote deletions were processed.
Server configuration
- Nextcloud version:
33.0.5.1/ version string33.0.5 - Database: PostgreSQL
- Background jobs: cron
- App/code path involved:
apps/dav/lib/CalDAV/CalDavBackend.php,apps/dav/lib/CalDAV/Federation/FederatedCalendarSyncService.php,apps/dav/lib/BackgroundJob/FederatedCalendarPeriodicSyncJob.php
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in apps/dav/lib/CalDAV/CalDavBackend.php at deleteCalendarObject(), then compare its calendar-type handling with createCalendarObject() and updateCalendarObject(). Reproduce or force the job through FederatedCalendarSyncService::syncOne() using the mentioned occ background-job command; done means federated deletions complete without the TypeError and the sync token advances.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- php
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 82/100