Skip to content

fix(core): stop ActiveSync log spam and folder loss on IMAP outage#193

Open
TDannhauer wants to merge 1 commit into
FRAMEWORK_6_0from
fix/activesync-imap-outage-log-spam
Open

fix(core): stop ActiveSync log spam and folder loss on IMAP outage#193
TDannhauer wants to merge 1 commit into
FRAMEWORK_6_0from
fix/activesync-imap-outage-log-spam

Conversation

@TDannhauer

Copy link
Copy Markdown
Contributor

Summary

  • Collapse the multi-layer ERR/WARN logging cascade on an IMAP outage down to a single NOTICE.
  • Distinguish transient IMAP connection failures from hard errors and surface them as Horde_ActiveSync_Exception_TemporaryFailure.
  • Stop returning an empty folder hierarchy on a transient failure (which could delete the client's mail folders); defer the FOLDERSYNC instead.

Motivation

With a device polling ActiveSync while the IMAP server is down, one connection failure was caught, logged, and re-thrown at every layer, spamming horde.log at ERR/WARN once per poll per device. getFolders() returning [] also caused FOLDERSYNC to diff against an empty hierarchy, risking removal of the user's mail folders on the device.

Changes

  • Horde_Core_ActiveSync_Imap_Factory::getMailboxes(): new _isTransientImapError() walks the exception chain (the underlying Horde_Imap_Client_Exception code survives inside wrappers such as the injector's "Cannot create IMP_Ftree"). Transient failures log once at NOTICE and throw Horde_ActiveSync_Exception_TemporaryFailure; other errors keep the ERR + Horde_ActiveSync_Exception behavior.
  • Horde_Core_ActiveSync_Driver::_getMailFolders(): drop the duplicate ERR re-log (kept a debug trace), re-throw preserving the exception type.
  • Horde_Core_ActiveSync_Driver::getFolders(): propagate TemporaryFailure instead of returning []; downgrade the non-transient log to debug.
  • Horde_Core_ActiveSync_Driver::getFolderList(): catch TemporaryFailure and return false, the state engine's "skip folder diff" signal.

Dependencies

Relies on horde/ActiveSync PR "report no folder changes when getFolderList() is false" so the false return is handled without a count(null) fatal. Merge that first.

Test plan

  • With IMAP reachable: FOLDERSYNC/SYNC behave as before.
  • With IMAP down: horde.log shows a single NOTICE per poll (not the ERR/WARN cascade); FOLDERSYNC returns no changes with the same synckey; device keeps its folders and recovers when IMAP returns.
  • Auth/config (non-connection) IMAP errors still log at ERR.

When the mail server is unreachable during ActiveSync polling, a single
IMAP connection failure was caught, logged at ERR/WARN, and re-thrown at
every layer (IMAP factory, _getMailFolders, getFolders), producing
several log lines per poll per device. getFolders() also returned an
empty array, which made FOLDERSYNC diff against an empty hierarchy and
could make clients drop all their mail folders.

Classify transient connection failures (DISCONNECT / SERVER_CONNECT /
LOGIN_UNAVAILABLE, detected via the preserved exception chain) in the
IMAP factory: log once at NOTICE and throw
Horde_ActiveSync_Exception_TemporaryFailure. Drop the redundant
re-logging in the driver, and have getFolderList() return false on a
temporary failure so the state engine skips folder-change detection this
round and the device retries later instead of losing folders. Genuine
(non-connection) errors are unchanged: still logged at ERR.
@TDannhauer TDannhauer requested a review from ralflang July 8, 2026 06:25
// the caller defer the sync instead of treating it as a
// hard error (which spams the log at every layer and can
// make clients drop their mail folders).
Horde::log(sprintf(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use Horde::log - inject a logger.

@ralflang

ralflang commented Jul 8, 2026

Copy link
Copy Markdown
Member

Hard-error IMAP failures still return [] from getFolders(). That would still allow hard errors to trigger clients deleting folders.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants