Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 27, 2026

Email Move Error Handling - Implementation Complete ✅

  • Understand the codebase structure and error flow
  • Modify ThreadEmailMover::processMailbox() to catch and handle move errors
    • Catch exceptions during email move operations
    • Send admin notification for each error
    • Continue processing next email instead of throwing
    • Track error count and stop after max 5 errors
    • Throw exception when max errors reached
  • Add tests for error handling behavior
    • Test that errors are caught and processing continues
    • Test that processing stops after 5 errors and throws exception
    • Test mixed errors and successes
  • Run existing unit tests to ensure no breaking changes
  • Address code review feedback (AdminNotificationService refactor)
  • Remove try-catch around admin notification per review feedback
  • Fix test to expect exception when max errors reached
  • Code review and security scan passed

Summary

Successfully implemented error handling for email move operations in the Offpost system. The solution ensures that:

  1. Individual email move errors (like malformed quoted-printable sequences) don't stop the entire email processing
  2. Administrators are notified of each error via email
  3. Admin notification failures will propagate as exceptions (not caught)
  4. Processing continues up to a maximum of 5 errors
  5. When 5 errors are reached, an exception is thrown to signal the problem
  6. All unit tests pass (15/15 tests, 90 assertions)

The changes are minimal and focused on the specific issue while maintaining backward compatibility.

Original prompt

We have error on moving emails. Handle the error by sending admin email, but continue to the next email to be move. Max 5 errors. An error occurred in the Offpost system:

Source: scheduled-imap-handling
Time: 2026-01-27 06:49:11 CET
Message: Unexpected error: Failed to move email UID 7828110 to folder INBOX.944439838-hoyanger-kommune - Klage paa maalrettet utestengelse av
journalisteosv: IMAP error during mailMove: Invalid quoted-printable sequence:
=?iso-8859-1?Q?9VnaXcqraedS9OkjiDGIHeRQeJbrUBBaNIUc1OllADN+4cW1FbhQD2EQjZ? =?iso-

Error Details:
file: /php-frontend/class/ThreadEmailMover.php
line: 111
stack_trace: #0 /php-frontend/update-imap-functions.php(130): ThreadEmailMover->processMailbox('INBOX', Array)
#1 /php-frontend/system-pages/scheduled-imap-handling.php(59): processInbox(Object(Imap\ImapConnection), Object(Imap\ImapFolderManager),
Object(Imap\ImapEmailProcessor), Array)
#2 /php-frontend/webroot/index.php(38): require('/php-frontend/s...')
#3 {main}


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 2 commits January 27, 2026 07:00
Co-authored-by: HNygard <168380+HNygard@users.noreply.github.com>
…eated instantiation

Co-authored-by: HNygard <168380+HNygard@users.noreply.github.com>
Copilot AI changed the title [WIP] Handle email move errors and notify admin Handle email move errors gracefully with admin notification Jan 27, 2026
Copilot AI requested a review from HNygard January 27, 2026 07:04
…pagate

Co-authored-by: HNygard <168380+HNygard@users.noreply.github.com>
Copilot AI requested a review from HNygard January 27, 2026 08:09
@HNygard HNygard marked this pull request as ready for review January 27, 2026 12:12
Copilot AI review requested due to automatic review settings January 27, 2026 12:12
@HNygard
Copy link
Owner

HNygard commented Jan 27, 2026

@copilot : Fix tests.

There was 1 error:

1) ThreadEmailMoverTest::testProcessMailboxStopsAfterMaxErrors
Exception: ThreadEmailMover: Maximum error count (5) reached, stopping email processing

/home/runner/work/offpost/offpost/organizer/src/class/ThreadEmailMover.php:153
/home/runner/work/offpost/offpost/organizer/src/tests/ThreadEmailMoverTest.php:720

ERRORS!
Tests: 414, Assertions: 1425, Errors: 1, Skipped: 2.

Co-authored-by: HNygard <168380+HNygard@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jan 27, 2026

@copilot : Fix tests.

...

Fixed. Updated testProcessMailboxStopsAfterMaxErrors to expect the exception that's now thrown when max error count is reached. All tests pass. (commit e53ba58)

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR enhances error handling in the email moving flow so that individual IMAP move failures are handled gracefully, with admin notifications, while allowing processing to continue up to a bounded number of errors.

Changes:

  • Extend ThreadEmailMover to track move errors, send admin notifications (via AdminNotificationService) on each failure, and stop processing once a configurable maximum error count (5) is reached.
  • Update the move logic to log errors, lazily initialize AdminNotificationService, and request folder status updates only on successful moves.
  • Add and adjust unit tests in ThreadEmailMoverTest to cover single-error continuation, max-error stopping behavior, and mixed success/error scenarios.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
organizer/src/class/ThreadEmailMover.php Adds AdminNotificationService dependency, introduces MAX_ERRORS, wraps moveEmail in error-handling that logs, notifies admins on each failure, continues processing, and throws once the maximum error count is reached, while still updating ImapFolderStatus on successful moves.
organizer/src/tests/ThreadEmailMoverTest.php Adjusts the existing move-error test to expect graceful completion instead of bubbling the exception, and adds new tests for continuing after a single error, stopping after 5 errors, and handling mixed successes and failures.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +685 to +724
public function testProcessMailboxStopsAfterMaxErrors() {
// Create 7 mock emails, all will fail to move
$mockEmails = [];
for ($i = 1; $i <= 7; $i++) {
$mockEmail = $this->createMock(\Imap\ImapEmail::class);
$mockEmail->uid = $i;
// Only first 5 emails will have their methods called
if ($i <= 5) {
$mockEmail->expects($this->once())
->method('getEmailAddresses')
->willReturn(["test{$i}@example.com"]);
}
$mockEmails[] = $mockEmail;
}

$this->mockEmailProcessor->expects($this->once())
->method('getEmails')
->with('INBOX')
->willReturn($mockEmails);

// Only 5 emails will be processed before stopping
$this->mockConnection->expects($this->exactly(5))
->method('getRawEmail')
->willReturn('Raw email content');

// All move attempts will fail, but only 5 will be attempted
$this->mockFolderManager->expects($this->exactly(5))
->method('moveEmail')
->willThrowException(new Exception('Move failed'));

$emailToFolder = [];
for ($i = 1; $i <= 7; $i++) {
$emailToFolder["test{$i}@example.com"] = "INBOX.Test - Thread {$i}";
}

// Expect exception to be thrown after reaching max errors
$this->expectException(Exception::class);
$this->expectExceptionMessage('Maximum error count (5) reached, stopping email processing');

$this->threadEmailMover->processMailbox('INBOX', $emailToFolder);
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

testProcessMailboxStopsAfterMaxErrors assumes that processMailbox() will "complete" and return an array even after hitting the maximum error threshold, but the implementation now throws an Exception once $errorCount >= self::MAX_ERRORS (see ThreadEmailMover::processMailbox catch block). With 7 emails and all 5 first moves failing, this test will encounter an uncaught exception at the 5th error and never reach the assertions at lines 722–724. Either the test should be updated to expectException (verifying that processing stops after 5 errors via the thrown exception and mock call counts) or the implementation should be changed to break/return instead of throwing so the behavior matches the current test name and assertions.

Copilot uses AI. Check for mistakes.
@HNygard HNygard merged commit a91ce81 into main Jan 27, 2026
1 check passed
@HNygard HNygard deleted the copilot/handle-email-move-errors branch January 27, 2026 12:29
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