Issue Description
Creating a recurring calendar event through JMAP fails. CalendarEvent/set rejects the
JSCalendar recurrenceRules property with a invalidProperties SetError, even when the value
is exactly the canonical example from the JMAP Calendars specification.
The same recurring events are accepted without any problem over CalDAV (iCalendar RRULE),
where Stalwart pre-expands them into individual instances as documented. So the recurrence
support exists in the server — it is specifically the JMAP write path for recurrenceRules
that is broken.
This is the server-side root cause behind a downstream webmail bug report
(bulwarkmail/webmail#327, “CalDav import of recurring events fails”): the webmail correctly
maps the imported iCalendar RRULE to a JSCalendar recurrenceRules array and sends it via
CalendarEvent/set (batchCreate); the server rejects the property and all recurring events
silently drop out of the import.
Expected Behavior
The event is created (created), as required by the JMAP Calendars specification, which lists
recurrenceRules as a regular settable property of a CalendarEvent and uses this exact value
shape in its examples.
Actual Behavior
The event is not created; the server returns an invalidProperties SetError naming
recurrenceRules:
{
"methodResponses": [["CalendarEvent/set", {
"accountId": "<account-id>",
"notCreated": {
"probe": {
"type": "invalidProperties",
"description": "Invalid property.",
"properties": ["recurrenceRules"]
}
}
}, "0"]],
"sessionState": "…"
}
Supporting evidence (three independent checks)
- Minimal recurrence rejected — the spec-canonical
recurrenceRules
([{ "@type": "RecurrenceRule", "frequency": "weekly" }]) →invalidProperties: ["recurrenceRules"].
A second variant withbyDay([{ "@type": "NDay", "day": "mo" }]) is rejected identically,
so it is not abyDay/NDayshape problem — the property itself is not accepted. - Control event succeeds — an otherwise identical event without
recurrenceRules,
created on the same account and calendar via the sameCalendarEvent/setcall, returns
creatednormally. The create path works; onlyrecurrenceRulesis rejected. - CalDAV path works — recurring events created via CalDAV (iCalendar
RRULE) are accepted
and correctly pre-expanded by the server into individual instances, confirming recurrence
handling exists and the gap is JMAP-specific.
Reproduction Steps
Send a minimal, spec-conformant CalendarEvent/set create with a single weekly recurrence rule
(values taken verbatim from the JMAP Calendars draft example
[{ "@type": "RecurrenceRule", "frequency": "weekly" }]):
{
"using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:calendars"],
"methodCalls": [["CalendarEvent/set", {
"accountId": "<account-id>",
"create": {
"probe": {
"@type": "Event",
"calendarIds": { "<calendar-id>": true },
"title": "RRULE probe (minimal)",
"start": "2026-06-10T09:00:00",
"duration": "PT1H",
"timeZone": "Europe/Berlin",
"recurrenceRules": [{ "@type": "RecurrenceRule", "frequency": "weekly" }]
}
}
}, "0"]]
}
Stalwart Version
v0.16.x
Installation Method
Binary (Linux)
Database Backend
RocksDB
Blob Storage
RocksDB
Search Engine
Internal
Directory Backend
Internal
Additional Context
- Downstream report:
bulwarkmail/webmail#327— the webmail’s ICS import drops all recurring
events becauseCalendarEvent/batchCreatereturns this sameinvalidPropertieson
recurrenceRules. - Related: GitHub issue #2925 (updates/destroys targeting synthetic instance IDs are likewise
“not yet supported”). This report is distinct: it covers the create path — setting
recurrenceRuleson a master event — which fails earlier in the lifecycle and with a
different error (invalidProperties, i.e. the create handler does not recognize the property
as settable, rather than an explicit “not yet supported” stub). GitHub issue #2925 presupposes that a
recurring event already exists (e.g. created via CalDAV) and concerns editing its individual
occurrences. Together they map the same incomplete JMAP recurrence surface:
create a master ✗ (this issue), edit/delete a single instance ✗ (#2925), andrecurrenceRules
not round-tripped on read (below). - Reading side appears affected too: in a non-expanded
CalendarEvent/query
(expandRecurrences: false), recurring master events that were created via CalDAV are
returned without theirrecurrenceRules, while the expanded query
(expandRecurrences: true) does fan them out into individual instances. The synthetic-ID
expansion model that produces this is confirmed in #2925, sorecurrenceRulesappears not to
be round-tripped on either the read or the write side of the JMAP Calendar implementation,
even though the underlying RRULE data is stored and expanded.
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