nextcloud / nextcloud/server

[Bug]: CalDAV PUT of a new VEVENT with ATTENDEE + EXDATE fails with TypeError in sabre/vobject ITip\Broker

Open
#62,950 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

0. Needs triage 32-feedback bug feature: caldav
Dominant language
PHP
Stars
36.9k
Forks
5.2k
Avg merge
2d 3h
Merged PRs (30d)
713

Description

⚠️ This issue respects the following points: ⚠️
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
  1. Share a calendar from user A to user B with write access.
  2. As user B, PUT a new object into that calendar (not an update of an existing one), containing an ORGANIZER that is not user B, an ATTENDEE that is user B with PARTSTAT set to a value other than NEEDS-ACTION, and at least one EXDATE.
  3. 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.