CalDAV: calendar-query REPORT returns empty calendar-data for JMAP-created events

Issue Description

Calendar events created via JMAP (CalendarEvent/set) are returned by a CalDAV
calendar-query REPORT with an EMPTY calendar-data property (propstat status
HTTP/1.1 200 OK, getetag present, <C:calendar-data/> empty).

A direct GET on the same resource href returns the full iCalendar document.
Events created via CalDAV PUT (even with identical content, including JSPROP
lines) are returned correctly by the same REPORT.

This affects ALL JMAP-created events (plain events, recurring events - tested
both, with and without rscale/skip in the recurrenceRule). Practical impact:
CalDAV sync clients that rely on calendar-query never see the content of any
event created through a JMAP client (e.g. a webmail frontend), so those events
are silently missing from every CalDAV sync.

Expected Behavior

The calendar-data property in the REPORT response contains the iCalendar
representation of the event — identical to what a GET on the resource returns
(RFC 4791 §9.6).

Actual Behavior

<D:response>
<D:href>/dav/cal/user%40example.com/default/KVUUBO62mF</D:href>
<D:propstat>
<D:prop>
<D:getetag>“…”</D:getetag>
<C:calendar-data/> ← empty
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
</D:response>

GET on the same href returns the complete VCALENDAR/VEVENT (HTTP 200).

Reproduction Steps

  1. Create an event via JMAP:

curl -u user:pass https://mail.example.com/jmap/
-H ‘Content-Type: application/json’
-d ‘{“using”:[“urn:ietf:params:jmap:core”,“urn:ietf:params:jmap:calendars”],
“methodCalls”:[[“CalendarEvent/set”,{“accountId”:“”,
“create”:{“t1”:{“calendarIds”:{“”:true},
“title”:“Test event”,“start”:“2026-07-21T19:00:00”,
“timeZone”:“Europe/Berlin”,“duration”:“PT30M”}}},“c1”]]}’

  1. Run a calendar-query REPORT against the calendar collection:

curl -u user:pass -X REPORT https://mail.example.com/dav/cal/user%40example.com/default/
-H ‘Depth: 1’ -H ‘Content-Type: application/xml; charset=utf-8’
-d ‘<?xml version="1.0" encoding="utf-8"?>
<c:calendar-query xmlns:d=“DAV:” xmlns:c=“urn:ietf:params:xml:ns:caldav”>
<d:prop><d:getetag/><c:calendar-data/></d:prop>
<c:filter><c:comp-filter name=“VCALENDAR”><c:comp-filter name=“VEVENT”/></c:comp-filter></c:filter>
</c:calendar-query>’

  1. Observe: the new resource is listed with status 200 and a getetag, but calendar-data is empty.
  2. GET the same href - the full iCalendar document is returned.
  3. Counter-test: PUT an .ics with the same content via CalDAV — that resource’s calendar-data IS returned by the same REPORT.

Relevant Log Output

No relevant errors at default logging level; happy to provide trace logs on request.

Stalwart Version

v0.16.x

Installation Method

Binary (Linux)

Database Backend

RocksDB

Blob Storage

RocksDB

Search Engine

Internal

Directory Backend

Internal

I have reviewed the documentation and FAQ and confirm that my issue is NOT addressed there.

on

I have searched this support forum (open and closed topics) and confirm this is not a duplicate.

on

I understand that topics in this category are triaged by a bot first but a human reply will follow up. If I’d prefer a human-only reply, I’ll add the no-ai tag to my topic.

on

Fixed in v0.16.14 (not yet released).