updateRules reports success despite failed imports or unsuccessful configuration activation

Issue Description

In Stalwart 0.16.22, update_spam_rules() can return TaskResult::Success even when importing or activating the ruleset fails.

Reload failures are not reflected in the task outcome:

  • Err returned by reload_registry() is logged, but execution continues.
  • Ok(ReloadResult) containing configuration errors is ignored entirely, without checking has_errors().

Object-write failures handled by the failed counter also do not affect the final result. This pattern applies to SpamRule, SpamDnsblServer, SpamTag, HttpLookup, MemoryLookupKey and SpamFileExtension.

The function then emits RulesUpdated and returns TaskResult::Success(vec!).

This concerns those specific error paths. Other errors, including failures to fetch or parse the ruleset, already return failure.

Expected Behavior

The task should report a non-success outcome when any object import is counted as failed or when required settings/lookup activation fails.

Both Err and Ok(ReloadResult) with non-empty errors should be handled.

A successful task outcome should indicate that the import and required activation completed, rather than merely that execution reached the end of the function.

Actual Behavior

The task reports success despite the handled failures described above.

In the activation-failure case, objects may already have been written to the registry while the running server retains its previous settings or lookup stores. Stored configuration and active configuration can therefore differ even though the update task reports success.

For object-import failures, the failed count appears in the logged tally, but the task outcome remains Success.

Reproduction Steps

Suggested fault-injection tests against v0.16.22;

A. Reload returns Err

  1. Supply a valid ruleset containing a new SpamRule.
  2. Allow insertion to succeed, setting reload_settings=true.
  3. Inject an Err from the subsequent reload_registry() call.
  4. Inspect the task result.
    Expected: non-success. Current code path: the error is logged, followed by Success.

B. Reload returns configuration errors

  1. Repeat the successful insertion.
  2. Make reload_registry() return Ok(ReloadResult) with non-empty errors and without activating the new configuration.
  3. Inspect the task result.
    Expected: non-success. Current code path: the result is discarded, followed by Success.

Repeat the activation tests for lookup-store reloading.

C. Object import is counted as failed

  1. Supply a valid ruleset.
  2. Inject an object-write result that reaches a branch incrementing stats.failed.
  3. Allow the remaining operations to complete.
  4. Inspect the tally and task result.
    Expected: non-success. Current code path: failed is incremented, but the task returns Success.

Relevant Log Output

No runtime logs are attached. This is a source-level finding; the reproduction steps describe proposed fault-injection tests.

Stalwart Version

v0.16.x

Installation Method

Binary (Linux)

Database Backend

RocksDB

Blob Storage

RocksDB

Search Engine

Internal

Directory Backend

Internal

Additional Context

Object-import failure counters:

Reload handling and final Success:

Reload activation guards and ReloadResult error handling:

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

This confirms the report is accurate against the current source: update_spam_rules logs reload_registry errors but always continues to Ok(TaskResult::Success(vec![])) regardless of stats.failed counts or reload errors/ReloadResult error checking.

Given this is a source-level bug report (not a usage question), I don’t have a documentation page to cite for expected/fixed behavior, and this forum bot cannot file issues or confirm fixes. I’ll relay that this appears accurate based on the source but note it needs maintainer triage.

I confirmed this against the v0.16.22 source and the behavior described matches what is in crates/services/src/task_manager/spam_classifier.rs: reload errors from reload_registry are only logged (not propagated), Ok(ReloadResult) is not inspected for embedded errors, and per-object failed counters are tallied but never checked before the function returns TaskResult::Success(vec![]). So the report’s characterization of the current code path is correct as written.

This is a source-level defect report rather than a documentation or usage question, so there isn’t a docs page to point to for “expected” behavior here. A staff or core-team member will need to review this to confirm intended semantics for TaskResult on partial/reload failures and decide on a fix.

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

This has been fixed in v0.16.24 (yet to be released).