JMAP EventSource ping reports interval in milliseconds instead of seconds

Issue Description

JMAP EventSource ping events report their interval value in milliseconds rather than seconds.

Stalwart converts the requested ping interval to milliseconds for the internal timer, but then serializes that converted value directly into the SSE ping payload.

For example, a 30-second ping interval is emitted at the correct time but reports:

event: ping
data: {"interval": 30000}

Expected Behavior

RFC 8620 §7.3 specifies that the interval in a ping event is in seconds.

A 30-second interval should therefore report:

event: ping
data: {"interval": 30}

Actual Behavior

The ping occurs at the correct interval, but its advertised value is multiplied by 1000.

For example, ping=30 reports 30000. In test mode, ping=1 produces:

event: ping
data: {"interval": 1000}

instead of 1.

Reproduction Steps

  1. Connect to the authenticated JMAP eventSourceUrl advertised in the Session object.
  2. Set ping=30, for example with types=* and closeafter=no.
  3. Wait for the ping event.
  4. Observe that it reports "interval": 30000 rather than "interval": 30.

Relevant Log Output

No error is logged. The EventSource connection remains valid; only the protocol value in the ping event is incorrect.

Stalwart Version

v0.16.x

Installation Method

Built from source

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

This confirms the bug exactly as described.

This is a confirmed bug: interval is converted to milliseconds internally (ping * 1000) but that millisecond value is serialized directly into the SSE ping payload instead of the original seconds value. RFC 8620 §7.3 requires the interval field in the ping event to be in seconds, so Stalwart’s output is 1000x too large.

Looking at the code (crates/jmap/src/api/event_source.rs), the fix would be to keep the seconds value (ping, or std::cmp::max(ping, 30)) separate for the payload string while still using the milliseconds value for the Duration. This looks like a genuine implementation bug rather than a config/doc issue, so it will need a code fix upstream.

I’ll flag this for a human/staff follow-up since it requires a source change, not a configuration workaround.

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong.

Fixed in v0.16.21 (yet to be released).