Recurring events cannot be created on Stalwart 0.16 (recurrenceRule vs recurrenceRules) #30

Closed
opened 2026-08-25 15:21:59 +00:00 by jcoffey-dev · 1 comment
Owner

Found while diagnosing #26 against a live Stalwart 0.16.19, not reported by a user.

ihasmail writes recurrence as RFC 8984 does — recurrenceRules, an array. Stalwart 0.16 stores a single rule under recurrenceRule, singular, and rejects the array outright:

CalendarEvent/set create { recurrenceRules: [{ "@type": "RecurrenceRule", frequency: "weekly" }] }
  -> notCreated: { type: "invalidProperties", description: "Invalid property.", properties: ["recurrenceRules"] }

Three shapes were tried — with @type, without it, with interval — all rejected the same way. The singular form is accepted and stored:

CalendarEvent/set create { recurrenceRule: { "@type": "RecurrenceRule", frequency: "weekly", count: 3 } }
  -> created, and CalendarEvent/get returns "recurrenceRule": { "frequency": "weekly", "count": 3 }

Two consequences, both on 0.16:

  1. Creating or editing a recurring event fails, with the raw Invalid property in a toast. Anything that sets a repeat in the event editor hits this.
  2. Existing recurrence never displays. EventEditor seeds its preset from ev.recurrenceRules?.[0] and EventPopover renders "Repeats …" from the same, so a series that exists on the server looks like a plain event.

Also worth recording, since it shapes the fix: an expanded occurrence carries no rule at all. Querying a real weekly series with expandRecurrences: true returns instances with their own ids (eaaaaas, iaaaaas, maaaaas over base s) and a recurrenceId, but no recurrenceRule — only the master carries one. That is why #25 keys its series test on recurrenceId.

The mock advertises the RFC 8984 spelling, which is why nothing caught this — same root cause as the capability-placement bug recorded in the README.

Related: #26 is the same vocabulary mismatch in the participant properties (sendTo/email vs calendarAddress, replyTo vs organizerCalendarAddress), where Stalwart drops the property silently instead of erroring.

Rebuilt from: session transcript.

Found while diagnosing #26 against a live Stalwart 0.16.19, not reported by a user. ihasmail writes recurrence as RFC 8984 does — `recurrenceRules`, an array. Stalwart 0.16 stores a single rule under `recurrenceRule`, singular, and **rejects** the array outright: ``` CalendarEvent/set create { recurrenceRules: [{ "@type": "RecurrenceRule", frequency: "weekly" }] } -> notCreated: { type: "invalidProperties", description: "Invalid property.", properties: ["recurrenceRules"] } ``` Three shapes were tried — with `@type`, without it, with `interval` — all rejected the same way. The singular form is accepted and stored: ``` CalendarEvent/set create { recurrenceRule: { "@type": "RecurrenceRule", frequency: "weekly", count: 3 } } -> created, and CalendarEvent/get returns "recurrenceRule": { "frequency": "weekly", "count": 3 } ``` Two consequences, both on 0.16: 1. **Creating or editing a recurring event fails**, with the raw `Invalid property` in a toast. Anything that sets a repeat in the event editor hits this. 2. **Existing recurrence never displays.** `EventEditor` seeds its preset from `ev.recurrenceRules?.[0]` and `EventPopover` renders "Repeats …" from the same, so a series that exists on the server looks like a plain event. Also worth recording, since it shapes the fix: an expanded occurrence carries **no rule at all**. Querying a real weekly series with `expandRecurrences: true` returns instances with their own ids (`eaaaaas`, `iaaaaas`, `maaaaas` over base `s`) and a `recurrenceId`, but no `recurrenceRule` — only the master carries one. That is why #25 keys its series test on `recurrenceId`. The mock advertises the RFC 8984 spelling, which is why nothing caught this — same root cause as the capability-placement bug recorded in the README. Related: #26 is the same vocabulary mismatch in the participant properties (`sendTo`/`email` vs `calendarAddress`, `replyTo` vs `organizerCalendarAddress`), where Stalwart drops the property silently instead of erroring. <sub>Rebuilt from: session transcript.</sub>
Author
Owner

Fixed in #31, now in main (merged via #28, so this did not close automatically). Writes use recurrenceRule, reads accept either spelling, and the mock now rejects the array the way the real server does.

Fixed in #31, now in main (merged via #28, so this did not close automatically). Writes use `recurrenceRule`, reads accept either spelling, and the mock now rejects the array the way the real server does.
This repo is archived. You cannot comment on issues.