Skip to content

[Refactor] Make all SMTP 4yz Errors Retryable#1293

Open
nams1570 wants to merge 1 commit intodevfrom
add-421-retryable-email
Open

[Refactor] Make all SMTP 4yz Errors Retryable#1293
nams1570 wants to merge 1 commit intodevfrom
add-421-retryable-email

Conversation

@nams1570
Copy link
Copy Markdown
Collaborator

@nams1570 nams1570 commented Mar 26, 2026

Context

The emails-low-level.tsx file is where the sending logic of an email over smtp occurs. It tries to send an email, and if there is an issue it has logic to mark the email as retryable or not. Then, the email-queue-step itself handles the actual retry efforts based on this flag and a few other factors (like if max retries have been attempted). Currently, if an error code isn't explicitly accounted for, it falls back to being an "unknown error while sending" and is marked as not retryable.
However, SMTP defines the 4yz class of error codes as being retryable.

Summary of Changes

We add fallback logic at the end of the responseCode specific block to mark the 4yz class of error codes as retryable. While the message here is less specific, that is okay since any codes we want to explicitly account for are handled above it. The main thing here is that these should be retried.

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of temporary email delivery failures: SMTP 4xx responses are now recognized as transient, returned as retryable temporary errors (including server response text), so the system can automatically retry and improve delivery reliability during brief server issues.

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 26, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
stack-auth-hosted-components Ready Ready Preview, Comment Mar 26, 2026 5:21pm
stack-backend Ready Ready Preview, Comment Mar 26, 2026 5:21pm
stack-dashboard Ready Ready Preview, Comment Mar 26, 2026 5:21pm
stack-demo Ready Ready Preview, Comment Mar 26, 2026 5:21pm
stack-docs Ready Ready Preview, Comment Mar 26, 2026 5:21pm

@nams1570 nams1570 marked this pull request as ready for review March 26, 2026 17:04
Copilot AI review requested due to automatic review settings March 26, 2026 17:04
@nams1570 nams1570 requested a review from N2D4 March 26, 2026 17:04
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 26, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c5fe35bd-b35d-4a1d-acd2-2ff2438a8cdf

📥 Commits

Reviewing files that changed from the base of the PR and between 2d4a7d1 and 15b9abf.

📒 Files selected for processing (1)
  • apps/backend/src/lib/emails-low-level.tsx
✅ Files skipped from review due to trivial changes (1)
  • apps/backend/src/lib/emails-low-level.tsx

📝 Walkthrough

Walkthrough

Added is4yzSMTPResponseCode(code) and a new branch in SMTP send error handling that treats 4xx numeric response codes (400–499) as transient negative completion replies, returning Result.error with errorType: 'TRANSIENT_NEGATIVE_COMPLETION_REPLY', canRetry: true, and a temporary error message.

Changes

Cohort / File(s) Summary
SMTP error handling
apps/backend/src/lib/emails-low-level.tsx
Added is4yzSMTPResponseCode(code) helper and a fallback branch that maps 4xx SMTP response codes to TRANSIENT_NEGATIVE_COMPLETION_REPLY with canRetry: true and a temporary error message (includes server response).

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Suggested reviewers

  • BilalG1
  • N2D4

Poem

🐰 I hopped through logs with ears so keen,
Caught 4xx whispers where they’d been unseen,
Marked them transient, told them to rest,
Retry later — that’s my little quest! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: making SMTP 4yz error codes retryable, which aligns with the primary objective of the PR.
Description check ✅ Passed The description provides clear context, explains the problem, references the SMTP specification, and summarizes the solution. It includes sufficient detail about the change and its impact.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch add-421-retryable-email

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 26, 2026

Greptile Summary

This PR adds fallback handling for SMTP 4yz response codes in emails-low-level.tsx so that any unrecognised temporary-class server error is marked canRetry: true rather than falling through to the non-retryable "unknown error" path. The change is well-targeted and correctly placed — it sits after all explicit per-code handlers and before the string-based temporaryErrorIndicators block, so it only fires when no more-specific rule has already matched.

Key points:

  • A small helper is4yzErrorCode(code) is introduced with correct range and type-guard logic.
  • The new errorType: 'TRANSIENT_NEGATIVE_COMPLETION_REPLY' is used purely for logging/display; downstream queue logic branches only on canRetry, so no existing behaviour is broken.
  • The error message "The email server is too busy" is a slight over-fit — not all 4xx codes mean the server is busy — consider a more neutral phrasing.
  • The fallback silently absorbs previously-uncaptured 4xx codes, removing the captureError signal that would have alerted the team to novel codes worth explicitly handling.

Confidence Score: 5/5

Safe to merge — the change is a targeted, correctly-placed fallback with no risk of breaking existing retry or failure logic.

The logic is sound: is4yzErrorCode is correctly implemented, the fallback is placed after all explicit handlers, and canRetry is the only field downstream code branches on. Both open comments are P2 style/observability suggestions that don't affect correctness or production reliability.

No files require special attention.

Important Files Changed

Filename Overview
apps/backend/src/lib/emails-low-level.tsx Adds a helper is4yzErrorCode and a fallback branch inside the instanceof Error block to mark unrecognised SMTP 4xx response codes as retryable — correctly placed after all explicit per-code handlers; minor UX and observability concerns noted.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[sendMail throws Error] --> B{error instanceof Error?}
    B -- No --> G[temporaryErrorIndicators check]
    B -- Yes --> C{code / errno checks\nEDNS / EAUTH / ETIMEDOUT\nEENVELOPE / socket close}
    C -- Matched --> D[Return specific Result.error\ncanRetry per code]
    C -- No match --> E{is4yzErrorCode\nresponseCode?}
    E -- Yes --> F[Return TRANSIENT_NEGATIVE_COMPLETION_REPLY\ncanRetry: true NEW]
    E -- No --> G
    G -- indicator match --> H[Return UNKNOWN\ncanRetry: true]
    G -- no match --> I[captureError\nReturn UNKNOWN\ncanRetry: false]
Loading

Reviews (1): Last reviewed commit: "refactor: retry all 4yz smtp errors" | Re-trigger Greptile

Copy link
Copy Markdown
Contributor

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

Refactors low-level SMTP send error handling to ensure SMTP transient (4xx / “4yz”) responses are treated as retryable, preventing “unknown send error” from prematurely stopping retries in the outbox queue flow.

Changes:

  • Added a helper to detect SMTP 4xx response codes.
  • Added fallback handling to mark any uncaptured 4xx SMTP response as retryable with a transient error type/message.

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/backend/src/lib/emails-low-level.tsx`:
- Around line 178-187: The message for 4yz SMTP fallback is too specific; update
the Result.error produced when is4yzErrorCode(responseCode) returns true (the
block that returns errorType 'TRANSIENT_NEGATIVE_COMPLETION_REPLY' via
Result.error) to use a neutral transient SMTP message such as "Temporary SMTP
error; please retry later." Preserve canRetry: true and rawError: error and keep
the same errorType so only the user-facing message changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ded2a029-218e-4881-a664-f55cfd86c4cc

📥 Commits

Reviewing files that changed from the base of the PR and between 371f9af and 2d4a7d1.

📒 Files selected for processing (1)
  • apps/backend/src/lib/emails-low-level.tsx

By definition 4yz class errors in SMTP are retryable
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