{{ message }}
Repository navigation
fix(mails): finish the job when a project's own SMTP server fails - #14139
Merged
Merged
Conversation
A project's SMTP credentials or host are theirs to fix, so retrying cannot help and the failed list only grows. Cloud SMTP failures still fail the job.
Contributor
🔵 Tier A · Mergeable after minor fixes
Project-owned SMTP delivery failures now record the error message on the mail span and return normally, while cloud SMTP failures continue to throw. The regression test exercises a project SMTP connection failure and checks that the cloud adapter is not used.
Fix with agent prompt### Issue 1
tests/unit/Platform/Workers/MailsTest.php:169-177
**Cover the worker failure through the e2e tier**
AGENTS.md explicitly forbids unit-testing workers and requires e2e coverage for queue-visible behavior. Please move this regression to the e2e tier and verify that the failed SMTP job completes without entering the failed queue; calling `Mails::action()` directly bypasses that lifecycle.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.📂 Walkthrough · 2
Reviewed |
✨ Benchmark resultsComparing
Per-scenario breakdown & investigation detailsMetrics below reflect the current branch (after). Δ P95 compares against the base.
Top API waits (after) |
abnegate
added a commit
that referenced
this pull request
Oct 6, 2026
Brings in #14130 (route physical collection reads, schema checks, cache purges and transaction commits through the databases handle), #14120's merge commit, #14137 (VCS tag globs) and #14139 (project SMTP failures finish the mail job). Conflicts were resolved by keeping the database 8.0 forms the train already uses (ColumnType cases, Capability checks via supports(), Attribute::fromArray for checkAttribute, supportsSpatial/ supportsRelationships helpers) while taking main's databases-handle routing, so dedicated databases get their own adapter's capabilities and schema. The spatial capability check moves after the database lookup because it now needs that database's adapter. Documents Create/Update keep the train's RelationshipValues, which already reads stored related documents through the databases handle; main's inline permission walk is superseded by it. Transactions increment/decrement now build their Counter from the databases handle, since main renamed the handler parameter and the counted collection's schema lives on that handle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Why
When a project sends mail through its own SMTP server and that server fails, the Mails worker throws. The queue then puts the job on the failed list (
utopia-queue.failed.v1-mails), as if the worker had broken.The cause is on the customer's side. The SMTP server rejects the login, or the host can't be reached. A retry would hit the same server with the same credentials, and nothing retries this list automatically anyway. The failed list only grows, and it buries real failures. It also keeps the
Queue has failed jobsalert firing on fra1mailsaround the clock.Production, last 24h:
A 6h sample of the fra1 project-SMTP errors:
Only project SMTP is affected
$typeissmtponly when the payload carries ansmtpconfig, and every publisher fills that from the project's own settings:smtpfrom$project->getAttribute('smtp'), and only whenenabledis true. Otherwise it stays[]and the mail goes through_APP_SMTP_HOST, which is our cloud SMTP and still throws.Project/SMTP/Tests/Create(the console's "send test email") uses the host and credentials the user entered.Mailsoverride doesn't touchsmtp.In production, all 649
smtpfailures in the last 24h came from 15 customer projects. One project with locked-out mailersend credentials accounted for 557 of them. None came fromconsole. The singlecloudfailure still throws.What
The worker already told the two cases apart: project SMTP threw with code 401 and cloud SMTP with 500. Project SMTP failures now record the error on the span and finish the job, the same way #13831 handles mail no provider can deliver. Cloud SMTP failures still throw. Those are ours, and retrying them makes sense.
The failure stays visible: the span keeps
mail.status: failure,mail.smtp_type: smtpand the error message.Test
testProjectSmtpFailureIsNotRetriedsends through a project SMTP server on a closed local port (127.0.0.1:1). Without the fix it throwsError sending mail: No SMTP host answered: 127.0.0.1:1 (… Connection refused), the same shape as production. With the fix the job finishes, and the cloud adapter is never used. The existing cloud-failure tests still expect a throw.vendor/bin/phpunit tests/unit/Platform/Workers/MailsTest.php: 6 tests passcomposer lint, PHPStan and Rector (tests/tools/rector.php) pass on the changed filesFollow-up
The functions worker does the same thing for customer runtime errors: execution timeouts, startup crashes and memory kills. That needs its own PR.
Related: #14140