Issue Description
When a JMAP client sets the JSCalendar recurrenceRule properties
rscale: “gregorian” and skip: “omit” (these are the RFC 8984 DEFAULT values;
real-world example: the Bulwark webmail client always sends them), Stalwart’s
iCalendar export renders the rule as e.g.
RRULE:FREQ=MONTHLY;INTERVAL=1;BYDAY=2TU;WKST=MO;SKIP=OMIT
while the rscale value is exported separately as
JSPROP;JSPTR=recurrenceRule/rscale:“gregorian”
RFC 7529 §4.1 requires: the SKIP rule part MUST NOT be specified unless the
RSCALE rule part is specified. The emitted RRULE is therefore invalid, and
strict parsers reject it (e.g. sabre/vobject: “Not supported: SKIP”), which
breaks recurrence expansion in downstream CalDAV clients.
Creating the same event WITHOUT rscale/skip produces a clean RRULE
(RRULE:FREQ=WEEKLY), so the problem is only triggered when the client sends
these (perfectly legal) JSCalendar defaults.
Expected Behavior
Either omit SKIP=OMIT/RSCALE=GREGORIAN entirely on export (they are the
defaults, semantics are identical), or emit RSCALE=GREGORIAN together with
SKIP in the RRULE so the rule stays valid per RFC 7529.
Actual Behavior
RRULE:FREQ=MONTHLY;INTERVAL=1;BYDAY=2TU;WKST=MO;SKIP=OMIT (no RSCALE)
JSPROP;JSPTR=recurrenceRule/rscale:“gregorian”
Reproduction Steps
- Create a recurring event via JMAP with the RFC 8984 default values set explicitly:
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”:“Recurring test”,“start”:“2026-07-21T19:00:00”,
“timeZone”:“Europe/Berlin”,“duration”:“PT30M”,
“recurrenceRule”:{“@type”:“RecurrenceRule”,“frequency”:“monthly”,
“interval”:1,“rscale”:“gregorian”,“skip”:“omit”,
“firstDayOfWeek”:“mo”,“byDay”:[{“day”:“tu”,“nthOfPeriod”:2}]}}}},“c1”]]}’
- GET the created resource via CalDAV.
- Observe SKIP=OMIT in the RRULE without an RSCALE part.
Relevant Log Output
No relevant errors at default logging level.
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