Issue Description
On first start Stalwart creates an MtaQueueQuota “Global queue quota” with enable=true, 100000 messages and 10737418240 bytes, but with an empty match expression. Quotas with an empty expression are skipped, so neither limit is enforced.
x:MtaQueueQuota/get returns for this object:
{“enable”: true, “description”: “Global queue quota”, “key”: {}, “match”: {“match”: {}, “else”: “”}, “messages”: 100000, “size”: 10737418240}
Code (v0.16.24):
- crates/common/src/manager/defaults.rs L101-113: the quota is created with the match expression left at its default (empty).
- crates/common/src/expr/if_block.rs L97: an expression with an empty “else” and no “match” entries compiles to an empty IfBlock.
- crates/smtp/src/queue/quota.rs L130-177: limits are checked and quota accounting is recorded only when !quota.expr.is_empty() && eval_if(…). With an empty expression the function returns true without checking or recording anything.
Inbound and outbound throttles treat an empty expression the opposite way: they apply when the expression is empty or true (crates/smtp/src/core/throttle.rs L182, crates/smtp/src/queue/throttle.rs L30).
The documentation’s global quota example uses the same limits with “match”: {“else”: “true”}: Quotas | Stalwart
Expected Behavior
The enabled global queue quota created at setup enforces its message and size limits.
Actual Behavior
The quota is shown as enabled with its limits, but check_quota() skips it, so the queue has no global limit.
Reproduction Steps
- Start a new Stalwart v0.16.24 installation.
- Read the “Global queue quota” with x:MtaQueueQuota/get: enable is true, the limits are set, and match is {“match”: {}, “else”: “”}.
- Follow the expression through compile_expr() and check_quota(): the empty IfBlock bypasses both limits.
Stalwart Version
v0.16.x
Installation Method
Binary (Linux)
Database Backend
RocksDB
Blob Storage
RocksDB
Search Engine
Internal
Directory Backend
Internal
Additional Context
The quota is created only when no MtaQueueQuota object exists (defaults.rs L101), so installations that already hold it keep the empty expression unless existing objects are also covered.
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