Issue Description
For an account with no CalendarEventNotification history, CalendarEventNotification/get returns a state that a subsequent CalendarEventNotification/changes call cannot use.
In the observed case, /get returned:
saa
which Stalwart encodes as State::Exact(0).
Calling /changes with that same value as sinceState returns:
{
"type": "cannotCalculateChanges",
"description": "The server cannot calculate the changes between the old and new states."
}
A full resync returns the same state again, so the failure repeats on the next incremental sync.
The current implementation appears to cause this because the empty notification changelog is converted to change id 0, while the generic /changes path later treats a non-initial state with no changelog entry as invalid.
Source trace:
crates/groupware/src/cache/calcard.rs,build_scheduling_resources:get_last_change_id(...)returnsNonewhen there is no notification changelog andunwrap_or_default()converts this to change id0.crates/jmap/src/calendar_event_notification/get.rs: the response state is taken fromcache.get_state(false), turning change id0intoState::Exact(0)(saa).crates/jmap/src/changes/get.rs: the current-state short circuit for an exactsinceStatehandlesCalendar,AddressBook, andEmail, but notCalendarEventNotification.- The subsequent changelog lookup for
Query::Since(0)is empty, leavingfrom_change_id == 0. The generic/changescode then treats this non-initial state as invalid and returnscannotCalculateChanges.
This means the empty notification collection is represented by a non-initial state which Stalwart itself issues but cannot subsequently process while the collection remains unchanged.
Expected Behavior
CalendarEventNotification/changes should accept a recently returned state from CalendarEventNotification/get.
If no changes have occurred, I would expect an empty successful response with the same old and new state.
RFC 8620 permits cannotCalculateChanges, but recommends that servers support calculating changes from state strings recently issued to clients.
References:
- RFC 8620, Section 5.2
- draft-ietf-jmap-calendars-26, Section 7.2
Actual Behavior
CalendarEventNotification/get returns saa, but an immediate CalendarEventNotification/changes using sinceState: "saa" returns cannotCalculateChanges.
Repeating a full /query + /get reconciliation returns the same unusable state.
Reproduction Steps
- Use an account with no
CalendarEventNotificationhistory. - Call
CalendarEventNotification/get. - Save the returned
state. - Without making any changes, call
CalendarEventNotification/changesusing that value assinceState. - Observe
cannotCalculateChanges. - Call
/getagain and observe that the same state is returned.
Relevant Log Output
Nothing from Stalwart.
Stalwart Version
v0.16.x
Installation Method
Docker
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