Skip to content

[Bug]: Accepting a recurring series via Thunderbird leaves the moved occurrence at NEEDS-ACTION #61114

@ndo84bw

Description

@ndo84bw

Bug description

In a recurring event of 4 occurrences where one occurrence has been moved by the organizer (RECURRENCE-ID override), accepting the invitation in Thunderbird with a single click on "Attend" / "Teilnehmen" results in 3 of 4 occurrences ending up with PARTSTAT=ACCEPTED. The moved occurrence stays at PARTSTAT=NEEDS-ACTION on both the attendee's and the organizer's calendar after the CalDAV sync settles.

From the attendee's perspective, the invitation appears in Thunderbird as one item covering the whole series including the modified occurrence. Clicking the single "Attend" button intuitively reads as "I am attending the series." The resulting partial-accept state was unexpected.

It is not obvious from the outside whether the responsibility for this lies on the server side (the receiving Nextcloud Sabre iTip handler) or on the client side (Thunderbird sending only one VEVENT in the REPLY). This issue is meant as a starting point for that discussion and contains the captured ICS so it can be analyzed against the spec.

Steps to reproduce

  1. As organizer (Calendar 6.4.2 on Nextcloud 33.0.3), create a recurring event with RRULE:FREQ=DAILY;COUNT=4, no attendees. Save.
  2. Open occurrence 2 (NR-2), push its DTSTART by one hour, choose "Update this occurrence". Save.
  3. Open occurrence 1 (NR-1, the master start), add one attendee with a real mail address, choose "Update this and all future". Save.
  4. As the attendee, sync the CalDAV calendar in Thunderbird so the iTip REQUEST in the inbox is consumed.
  5. Click "Attend" / "Teilnehmen" on the invitation once.
  6. Wait for the CalDAV sync to settle on both sides.

Expected behavior

After accepting the invitation once, all four occurrences are marked accepted on both the attendee's and the organizer's calendar.

Actual behavior

After accepting the invitation once and waiting for CalDAV sync:

  • Master (SEQUENCE: 4, attendee on it, RRULE for 4 occurrences): PARTSTAT=ACCEPTED.
  • Override at RECURRENCE-ID:20260623T100000 (the moved NR-2, SEQUENCE: 1, attendee on it): PARTSTAT=NEEDS-ACTION.

The moved occurrence is therefore the only one of the four whose status is not advanced.

Captured iTip REQUEST received by the attendee (the .ics the attendee acted on)

METHOD:REQUEST, captured from the invitation mail the attendee received after step 3 and clicked "Attend" on in step 5:

BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Sabre//Sabre VObject 4.5.6//EN
CALSCALE:GREGORIAN
METHOD:REQUEST
BEGIN:VTIMEZONE
TZID:Europe/Berlin
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
DTSTART:19700329T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
DTSTART:19701025T030000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
CREATED:20260609T142807Z
LAST-MODIFIED:20260609T142853Z
SEQUENCE:4
UID:00000000-0000-0000-0000-000000000000
DTSTART;TZID=Europe/Berlin:20260622T100000
DTEND;TZID=Europe/Berlin:20260622T103000
STATUS:CONFIRMED
SUMMARY:Daily 3
RRULE:FREQ=DAILY;COUNT=4
ATTENDEE;CN=Attendee;CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTI
 CIPANT;RSVP=TRUE:mailto:attendee@example.org
ORGANIZER;CN=Organizer:mailto:organizer@example.org
DTSTAMP:20260609T142853Z
END:VEVENT
BEGIN:VEVENT
CREATED:20260609T142831Z
LAST-MODIFIED:20260609T142853Z
SEQUENCE:1
UID:00000000-0000-0000-0000-000000000000
DTSTART;TZID=Europe/Berlin:20260623T110000
DTEND;TZID=Europe/Berlin:20260623T113000
STATUS:CONFIRMED
SUMMARY:Daily 3
RECURRENCE-ID;TZID=Europe/Berlin:20260623T100000
ATTENDEE;CN=Attendee;CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTI
 CIPANT;RSVP=TRUE:mailto:attendee@example.org
ORGANIZER;CN=Organizer:mailto:organizer@example.org
DTSTAMP:20260609T142853Z
BEGIN:VALARM
ACTION:DISPLAY
TRIGGER;RELATED=START:PT0S
X-NC-DEFAULT-ALARM:true
DESCRIPTION:This is an event reminder.
END:VALARM
END:VEVENT
END:VCALENDAR

Both master and override carry the same attendee with PARTSTAT=NEEDS-ACTION. After the attendee accepts and the CalDAV sync settles, only the master's PARTSTAT advances.

Open question for the discussion

Two interpretations of where the responsibility for the missing override PARTSTAT update lies:

  • Client-side (Thunderbird and other ICS-aware clients): the REPLY produced on a single Accept click contains only the master VEVENT. Under this reading, clients are expected to send a per-occurrence REPLY for every override the REQUEST contained, and the gap is in Thunderbird (and likely other clients).
  • Server-side (Nextcloud / Sabre): a REPLY whose only VEVENT targets the master could be expanded by the receiving iTip handler so that the same attendee's PARTSTAT is also advanced on every existing recurrence-exception of the same UID, unless the override already carries an explicit divergent PARTSTAT.

Either interpretation matches some reading of RFC 5546; an explicit position from maintainers would help decide whether this issue should track a server-side change or be redirected upstream.

Server / environment

Additional info

This scenario surfaced while validating the fix for nextcloud/calendar#8450. The #8450 fix is the prerequisite for the override carrying the attendee at all; without it the REPLY scenario above cannot be observed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    0. Needs triagePending check for reproducibility or if it fits our roadmapbugfeature: caldavRelated to CalDAV internals

    Type

    No fields configured for Bug.

    Projects

    Status
    To triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions