When there is mails in the queue, retry now or changing the queue mail attempts does not work because the retry datetime is not pasted. When I try to change it from the dashboard it does not work as well. It seems there is no option to force requeue of messages before the retry datetime.
Expected Behavior
Retry now would reset all queue messages to be sent now and changing the datetime of retry would work.
Actual Behavior
Retry now does not work nor changing the datetime retry.
Reproduction Steps
Configure Stalwart classic
Prepare a mail to be sent later or disable blob store to force an incoming message to be on the queue
Try to change queue datetime or retry to send the message.
Relevant Log Output
Just delivery started and delivery ended without nothing done.
Stalwart Version
v0.16.x
Installation Method
Binary (Linux)
Database Backend
MySQL
Blob Storage
MySQL
Search Engine
MySQL
Directory Backend
SQL
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 agree to follow the project’s Code of Conduct.
Can confirm this, but I noticed that even if a change in retry time is not reflected in the UI, I could see a retry being logged at the set time. So it looks to me like a UI issue.
I can’t reproduce. Both “Retry All” from the list or manually setting a retry time while viewing the message works. Please try updating to the latest webUI. If the problem persists enable the Dev Console and provide the JMAP request/responses.
Were you trying to reschedule delivery for a recipient that had permanent failures perhaps? That’s the only issue I could find in the code and it has now been fixed.
The first time it was local emails with temporary faille because blob had an issue.
I tried now using a fake server with greylist so again a temporary failure. I have in logs “delivery started” but then it ends directly. However when the retry time arrives, the delivery starts normally.
I don’t think reschedule for permanent failure is interesting. A user interaction should be needed.
There seems to be a UI bug, but… There also seems to be a backend bug as well, appearing to do the retry, but it’s just an empty start/end cycle with no actual work.
Two functions disagree on what “due” means:
Queue index key (Message::next_events(), queue/manager.rs:313) — due = min(retry.due, notify.due, expires) per recipient/queue.
When notify.due < retry.due, the index entry fires (due <= now), try_deliver emits AttemptStart/AttemptEnd, but the guard sees retry.duestill in the future → falls into the _ => arm → save_changes rewrites the same past index key → Deferred → immediate re-dispatch. No DomainDeliveryStart, no SMTP.
It never self-heals because notify.due is only advanced inside build_dsn (queue/dsn.rs:287 and :411), and build_dsn early-returns None when no DSN text was produced (dsn.rs:~192). DSN text for the delay case is gated on rcpt.has_flag(RCPT_NOTIFY_DELAY) (dsn.rs:156, :172). So any recipient withoutNOTIFY=DELAY whose initial notify.due (created + notify_intervals[0]) has elapsed has a permanently-past notify.due → permanent index entry in the past → tight redispatch loop hammering the store for the entire deferral window. A manual retry that sets retry.due = now masks it briefly (guard passes once) but the loop resumes on the next temp-failure.
next_delivery_event must mirror next_events() (min(retry, notify, expires)), or deliver_task must branch on the notify/expiry components instead of bailing.
I am not sure about your arguments but yes you’re right it triggers AmmptStart and AttemptEnd without doing everything and notify is indeed before retry. I think the issue is inside the function who is managing retry due modifications.
The backend code has been fixed. I do not believe there are any issues in the UI code though, the next retry time was being saved properly but ignored because the DSN due time was outdated.
Still seeing this in 0.16.8 when the message’s recipients are in a virtual queue (e.g. `remote`): “Retry Now” reports success, but nothing is persisted, and the scheduler only logs an empty 0 ms `attempt-start`/`attempt-end` pair.
Could it be that `queued_message_set()` wraps the message with `QueueName::default()`? That would make `is_multi_queue` true for recipients in a non-default queue, and the merge in `save_changes()` filters recipients by the wrapper’s queue name — which then matches nothing, so the unmodified message gets written back while the API still reports `updated`.
Patching it that way fixed it for me locally (used Claude for identifying potential causes and fixing attempts).
I can confirm this issue. I am currently using 0.16.8-26f41f8-oss. I cannot modify queued messages (e.g. the “Next Retry” property). “Retry Now” and “Retry All” do only log an attempt with 0ms without actually performing a retry. I have not found any workaround to directly perform a retry.