[Bug]: CalDAV PUT of a new VEVENT with ATTENDEE + EXDATE fails with TypeError in sabre/vobject ITip\Broker
Nobody has claimed this yet.
- Dominant language
- PHP
- Stars
- 36.9k
- Forks
- 5.2k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 713
Description
⚠️ This issue respects the following points: ⚠️
- This is a bug, not a question or a configuration/webserver/proxy issue.
- This issue is not already reported on Github OR Nextcloud Community Forum (I've searched it).
- Nextcloud Server is up to date. See Maintenance and Release Schedule for supported versions.
- I agree to follow Nextcloud's Code of Conduct.
Bug description
A CalDAV PUT that creates a new calendar object fails with HTTP 500 when the VEVENT contains both ATTENDEE and EXDATE properties, and the requesting user is an attendee whose PARTSTAT is already set to something other than NEEDS-ACTION.
Root cause: on creation ($isNew === true), OCA\DAV\CalDAV\Schedule\Plugin::calendarObjectChange() passes $currentObject = null into processICalendarChange(). Broker::parseEvent() then builds a default $oldEventInfo that is missing the exdate key:
$oldEventInfo = [
'organizer' => null,
'significantChangeHash' => '',
'attendees' => [],
];
Broker::parseEventForAttendee() accesses it unconditionally at line 686:
if (!in_array($exDate, $oldEventInfo['exdate'])) {
Since PHP 8.1, passing null as the haystack to in_array() raises a fatal TypeError instead of emitting a warning, so the request aborts with a 500 and the event is never created.
sabre-io/vobject master guards this with $oldEventInfo['exdate'] ?? [], but that is on the 5.x line and was never backported to 4.5. The copy currently in nextcloud/3rdparty master still contains the unguarded access, so this is not fixed in any current or upcoming Nextcloud release as far as I can tell.
Vendored dependency: sabre/vobject 4.5.6.
Related, but different: #59695 and #50843 touch the same area, but concern $eventInfo['status'] leaking from a cancelled recurrence override. This report is about the missing exdate key in the default $oldEventInfo on object creation.
I checked #50843: it modifies only apps/dav/lib/CalDAV/TipBroker.php and its test, and does not touch 3rdparty/sabre/vobject/lib/ITip/Broker.php. parseEventForAttendee() is not overridden in TipBroker (no occurrence in the file), so the faulty code path in Sabre's parent Broker is still reachable on NC 34+ as well.
Suggested fix: either backport the null-coalescing guard to the 4.5 branch of sabre/vobject and bump the vendored version, or add 'exdate' => [] to the default $oldEventInfo array. The latter is arguably more correct, since exdate is documented as part of the structure returned by parseEventInfo().
Workaround in use here: setting SCHEDULE-AGENT=CLIENT on all ATTENDEE properties. Attendees carrying that parameter are skipped in parseEventInfo(), so the faulty code path is never entered.
Steps to reproduce
- Share a calendar from user A to user B with write access.
- As user B,
PUTa new object into that calendar (not an update of an existing one), containing anORGANIZERthat is not user B, anATTENDEEthat is user B withPARTSTATset to a value other thanNEEDS-ACTION, and at least oneEXDATE. - The request fails with HTTP 500.
repro.ics:
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//repro//EN
BEGIN:VEVENT
UID:repro-exdate-attendee-001
DTSTAMP:20260805T100000Z
DTSTART;TZID=Europe/Berlin:20260810T100000
DTEND;TZID=Europe/Berlin:20260810T110000
RRULE:FREQ=WEEKLY;COUNT=5
EXDATE;TZID=Europe/Berlin:20260817T100000
SUMMARY:Repro
ORGANIZER;CN=User A:mailto:usera@example.com
ATTENDEE;CN=User B;PARTSTAT=ACCEPTED:mailto:userb@example.com
END:VEVENT
END:VCALENDAR
curl -u <userB>:<app-password> -X PUT \
-H "Content-Type: text/calendar; charset=utf-8" \
--data-binary @repro.ics \
https://cloud.example.com/remote.php/dav/calendars/<userB>/<calendar>/repro-exdate-attendee-001.ics
Note the PARTSTAT condition: for a new object, parseEvent() only calls parseEventForAttendee() if the attendee has a participation status other than NEEDS-ACTION. Without that, the faulty branch is never reached and the bug does not appear.
Expected behavior
The calendar object is created and the server responds with HTTP 201.
Nextcloud Server version
32
Operating system
Debian/Ubuntu
PHP engine version
PHP 8.3
Web server
Nginx
Database engine version
MariaDB
Is this bug present after an update or on a fresh install?
Upgraded to a MAJOR version (ex. 31 to 32)
Are you using the Nextcloud Server Encryption module?
Encryption is Disabled
What user-backends are you using?
- Default user-backend (database)
- LDAP/ Active Directory
- SSO - SAML
- Other
Configuration report
{
"system": {
"passwordsalt": "***REMOVED SENSITIVE VALUE***",
"secret": "***REMOVED SENSITIVE VALUE***",
"trusted_domains": [
"nextcloud.splendid-internet.de"
],
"datadirectory": "***REMOVED SENSITIVE VALUE***",
"dbtype": "mysql",
"version": "33.0.6.2",
"overwrite.cli.url": "https:\/\/nextcloud.splendid-internet.de",
"overwritehost": "nextcloud.splendid-internet.de",
"overwriteprotocol": "https",
"dbname": "***REMOVED SENSITIVE VALUE***",
"dbhost": "***REMOVED SENSITIVE VALUE***",
"dbtableprefix": "oc_",
"mysql.utf8mb4": true,
"dbuser": "***REMOVED SENSITIVE VALUE***",
"dbpassword": "***REMOVED SENSITIVE VALUE***",
"installed": true,
"instanceid": "***REMOVED SENSITIVE VALUE***",
"maintenance": false,
"maintenance_window_start": 4,
"default_phone_region": "+49",
"mail_smtpmode": "smtp",
"mail_smtphost": "***REMOVED SENSITIVE VALUE***",
"mail_sendmailmode": "smtp",
"mail_smtpport": "587",
"mail_smtpauth": true,
"mail_from_address": "***REMOVED SENSITIVE VALUE***",
"mail_domain": "***REMOVED SENSITIVE VALUE***",
"mail_smtpname": "***REMOVED SENSITIVE VALUE***",
"mail_smtppassword": "***REMOVED SENSITIVE VALUE***",
"config_preset": 7,
"dbdriveroptions": {
"1002": "SET NAMES utf8mb4 COLLATE utf8mb4_bin"
},
"theme": "",
"loglevel": "2",
"memcache.locking": "\\OC\\Memcache\\Redis",
"memcache.local": "\\OC\\Memcache\\Redis",
"memcache.distributed": "\\OC\\Memcache\\Redis",
"redis": {
"host": "***REMOVED SENSITIVE VALUE***",
"port": 6379,
"dbindex": 0
},
"trusted_proxies": "***REMOVED SENSITIVE VALUE***",
"defaultapp": "spreed,files",
"updater.release.channel": "stable",
"blacklisted_files": [],
"serverid": 1
}
}
List of activated Apps
Enabled:
- admin_audit: 1.23.0
- app_api: 33.0.0
- bruteforcesettings: 6.0.0
- calendar: 6.5.0
- calendar_resource_management: 0.12.1
- circles: 33.0.0
- cloud_federation_api: 1.17.0
- comments: 1.23.0
- contacts: 8.7.2
- contactsinteraction: 1.14.1
- dashboard: 7.13.0
- dav: 1.36.0
- event_update_notification: 2.9.0
- federatedfilesharing: 1.23.0
- federation: 1.23.0
- files: 2.5.0
- files_bpm: 1.8.2
- files_downloadlimit: 5.1.0
- files_fulltextsearch: 33.0.0
- files_pdfviewer: 6.0.0
- files_reminders: 1.6.0
- files_sharing: 1.25.2
- files_trashbin: 1.23.0
- files_versions: 1.26.0
- fileslibreofficeedit: 2.0.1
- firstrunwizard: 6.0.0
- fulltextsearch: 33.0.0
- fulltextsearch_elasticsearch: 33.0.0
- groupfolders: 21.0.9
- guests: 4.7.5
- logreader: 6.0.0
- lookup_server_connector: 1.21.0
- mail: 5.10.5
- notifications: 6.0.0
- notify_push: 1.3.3
- oauth2: 1.21.0
- password_policy: 5.0.0
- privacy: 5.0.0
- profile: 1.2.0
- provisioning_api: 1.23.0
- quota_warning: 1.24.0
- recommendations: 6.0.0
- related_resources: 4.0.0
- richdocuments: 10.2.0
- richdocumentscode: 26.4.104
- serverinfo: 5.0.0
- settings: 1.16.0
- sharebymail: 1.23.0
- spreed: 23.0.7
- support: 5.0.0
- survey_client: 5.0.0
- systemtags: 1.23.0
- tasks: 0.18.1
- text: 7.0.1
- theming: 2.8.0
- twofactor_backupcodes: 1.22.0
- twofactor_totp: 15.0.0
- twofactor_webauthn: 2.7.0
- updatenotification: 1.23.0
- user_status: 1.13.0
- viewer: 6.0.0
- weather_status: 1.13.0
- webhook_listeners: 1.5.0
- workflowengine: 2.15.0
Disabled:
- activity: 6.0.0 (installed 5.0.0-dev.0)
- announcementcenter: 7.3.0 (installed 7.3.0)
- appointments: 2.6.2 (installed 2.6.2)
- certificate24: 0.4.0 (installed 0.4.0)
- deck: 1.16.3 (installed 1.16.3)
- encryption: 2.21.0
- epubviewer: 1.8.1 (installed 1.8.1)
- files_antivirus: 6.1.0 (installed 6.1.0)
- files_external: 1.25.1
- integration_deepl: 2.1.0 (installed 2.1.0)
- nextcloud_announcements: 5.0.0 (installed 4.0.0-dev.0)
- officeonline: 3.1.1 (installed 3.1.1)
- passwords: 2025.12.21 (installed 2025.12.21)
- photos: 6.0.0 (installed 5.0.0-dev.1)
- suspicious_login: 11.0.0
- twofactor_nextcloud_notification: 7.0.0
- user_ldap: 1.24.0
- whiteboard: 1.5.6 (installed 1.5.6)
Nextcloud Signing status
Nextcloud Logs
TypeError: in_array(): Argument #2 ($haystack) must be of type array, null given
Sabre\VObject\ITip\Broker::parseEventForAttendee() Broker.php:686
<- Sabre\VObject\ITip\Broker::parseEvent() Broker.php:260
<- Sabre\CalDAV\Schedule\Plugin::processICalendarChange() Schedule/Plugin.php:616
<- OCA\DAV\CalDAV\Schedule\Plugin::calendarObjectChange() Schedule/Plugin.php:208
<- Sabre\CalDAV\Plugin::validateICalendar() (beforeCreateFile hook)
Observed in production, four occurrences, all PUT requests from the same client to a shared calendar (values redacted):
2026-08-05T10:44:13+00:00 <user> PUT /remote.php/dav/calendars/<user>/<calendar>/<uid>.ics
Additional info
No response
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 3rdparty/sabre/vobject/lib/ITip/Broker.php, reading parseEvent() and parseEventForAttendee() around the reported line, then reproduce the failure with the supplied repro.ics PUT request. Compare the vendored code with the described sabre/vobject guard and verify that a new VEVENT containing ATTENDEE and EXDATE is created with HTTP 201 without regressing existing scheduling behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- php
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 55/100