Email Trace Query is broken

Issue Description

When trying to query the Inbound or Outbound email traces no results are returned.
Same for the web dashboard, stalwart CLI or manual jmap querying.
Only when putting a text filter in results appear.
The date filter does not work at all, when set no result despite matching entries exist.
Eq, gt, lt, all the same.
QueueId is also not working.

I want to export traces for archival purposes with the jmap API and therefor want to query by specific dates.

Expected Behavior

When querying the Traces without a filter there should be results. The date filter should also work

Actual Behavior

Results only appear when using text filter, all other filters return 0 results

Reproduction Steps

  1. Receive emails
  2. Go to Inbound email traces
  3. List only shows the last like 24 hours or so, everything older disappears and can only be brought back with a text search

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

Let me research the email trace query functionality to give a grounded answer.

This is an excellent question that gets right to the heart of a specific behavior in the server code. Here’s what’s actually happening:

The “no results without a filter” behavior is by design (a 24-hour default window), but the timestamp date filter is likely broken for most date inputs.

Looking at the source code in crates/jmap/src/registry/mapping/telemetry.rs:

  1. Why an empty filter only shows ~24 hours of traces: When no meaningful filter is applied (no text, timestamp, or queueId condition), the server automatically injects a > now() - 86400 (last 24 hours) guard to avoid dumping the entire trace store. This is the fallback behavior, not a bug per se:

    tracing_query.push(SearchFilter::gt(
        SearchField::Id,
        SnowflakeIdGenerator::from_timestamp(now() - 86400).unwrap_or_default(),
    ));
    
  2. Why the timestamp date filter returns no results: The timestamp filter converts a date string via UTCDateTime::from_str(s), then uses SnowflakeIdGenerator::from_timestamp(). If SnowflakeIdGenerator::from_timestamp() returns None (which can happen for timestamps outside a certain range or format), the filter is silently skipped, and the fallback 24-hour window kicks in instead – returning nothing if your date is outside that window.

  3. Why text works: Text search goes through a different code path (keyword index) that does NOT have the 24-hour default, so it searches across all stored traces.

Workaround for archival exports by date: The text filter is currently the only reliably working filter for reaching older traces. For archival, you could try fetching by known IDs (Snowflake IDs encode timestamps, so IDs from a specific time period could be iterated), but that is not straightforward from the JMAP API alone.

This appears to be a bug in the timestamp filter implementation for x:Trace/query – specifically that failed SnowflakeIdGenerator::from_timestamp() conversions silently fall back to the 24-hour window rather than returning an error or an empty result. A human staff member should follow up on whether a fix is planned.

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

Is the problem that timestamp should be a full timestamp but the filter query is only a date? Can you confirm that from the code?

I got it working withh JMAP by providing a timestamp with date and time instead of only a date like the WebAdmin does.

Resending the request from the WebAdmin with a datetime then also filters correctly.