Skip to content

CalDAV/iCloud: PUT 404 when VEVENT has no DTEND (zero-duration event) #1200

@samm81

Description

@samm81

When syncing a zero-duration Google Calendar event (VEVENT with DTSTART but no DTEND) to an iCloud CalDAV collection, vdirsyncer’s upload fails with HTTP 404 Not Found on PUT. The same calendar accepts the item if I add either:

  • DTEND equal to DTSTART.

Zero-duration events without DTEND are valid iCalendar per RFC 5545, and Google emits them that way by default. This appears to be an iCloud server quirk. A small, optional normalization/transform on upload would make vdirsyncer interop with iCloud out of the box.

Environment

  • vdirsyncer version: vdirsyncer/0.20.0.post1.dev17+g3ba998e63.d20250920
  • Python: 3.13.1
  • OS: Linux
  • Destination server: iCloud CalDAV
    • Host: p143-caldav.icloud.com
    • Server header: AppleHttpServer/7347b443149f
    • Collection is shared but writable (see PROPFIND snippet below)
  • Source: Google Calendar (events created in Google as “instant” events)

What happens

  • vdirsyncer tries to upload an event that has no DTEND (Google “instant” event).
  • PUT to iCloud returns 404 Not Found, and vdirsyncer raises NotFoundError.
  • If I add DTEND with the same value as the DTSTART, the PUT returns 201 Created and the sync proceeds.

Expected

  • iCloud should accept VEVENT with DTSTART only (zero-duration), but since it doesn’t, vdirsyncer could optionally normalize zero-duration VEVENTs on upload to add an explicit end so iCloud accepts them.

Minimal reproducible ICS and curl

A) Failing: no DTEND

cat > /tmp/fail.ics <<'ICS'
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//test//EN
BEGIN:VEVENT
UID:[email protected]
DTSTAMP:20250101T000000Z
DTSTART:20250102T000000Z
SUMMARY:Zero duration without DTEND
END:VEVENT
END:VCALENDAR
ICS

curl -i -u 'APPLE_ID:APP_SPECIFIC_PASSWORD' \
  -X PUT \
  -H 'If-None-Match: *' \
  -H 'Content-Type: text/calendar; charset=utf-8' \
  --data-binary @/tmp/fail.ics \
  'https://p143-caldav.icloud.com/111.../calendars/aaa.../[email protected]'
# Returns: code=404

B) Succeeds: add DTEND equal to DTSTART

cat > /tmp/succeed_dtend_equal.ics <<'ICS'
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//test//EN
BEGIN:VEVENT
UID:[email protected]
DTSTAMP:20250101T000000Z
DTSTART:20250102T000000Z
DTEND:20250102T000000Z
SUMMARY:Zero duration with DTEND==DTSTART
END:VEVENT
END:VCALENDAR
ICS

curl -i -u 'APPLE_ID:APP_SPECIFIC_PASSWORD' \
  -X PUT \
  -H 'If-None-Match: *' \
  -H 'Content-Type: text/calendar; charset=utf-8' \
  --data-binary @/tmp/succeed_dtend_equal.ics \
  'https://p143-caldav.icloud.com/111.../calendars/aaa.../[email protected]'
# Returns: code=201

Additional observations

  • Google emits zero-duration events without DTEND by default. When I edited the event in Google to have a 15-minute duration, vdirsyncer synced it to iCloud successfully.

Proposal

I realize vdirsyncer generally avoids mutating items. However, to improve interop with iCloud, consider auto-"fixing" VEVENTs that are missing DTEND by adding a DTEND equal to DTSTART before PUT to CalDAV. This could be limited to requests that are known to being going to ical via server detection through the Server header AppleHttpServer.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions