fix(proxy): run SMTP send_email off the event loop with a connection timeout - #38473
Conversation
…timeout Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
|
|
PR #38473 has no labels (no |
Greptile SummaryThe PR moves blocking SMTP sessions off the event loop, applies a configurable connection timeout, and contains malformed timeout configuration within the email error boundary.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| litellm/proxy/utils.py | Moves synchronous SMTP work to a worker thread, forwards a connection timeout, and now catches malformed timeout configuration. |
| tests/test_litellm/proxy/utils/prisma_and_spend/test_send_email.py | Adds focused regression coverage for timeout configuration, malformed values, and off-event-loop execution. |
| tests/test_litellm/proxy/utils/prisma_and_spend/conftest.py | Extends the in-memory SMTP fixture to capture connection arguments and worker-thread identity. |
| tests/test_litellm/proxy/test_proxy_utils.py | Updates connection-factory tests to verify timeout forwarding for plain SMTP and SMTP over SSL. |
| codecov.yaml | Prevents stale CircleCI coverage from carrying forward between reports. |
Reviews (5): Last reviewed commit: "ci: stop carrying forward the dead circl..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…for timeout Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
Good catch, moved the float() inside the try in 75fdd7f and added a malformed SMTP_TIMEOUT regression test |
Pull request was closed
…itellm_smtp_send_email_timeout
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
@greptileai re-review: since the last reviewed commit the branch merged litellm_internal_staging and added codecov.yml (disable carryforward/join for the stale circleci flag); no product code changed. |
TLDR
Problem this solves:
How it solves it:
User Flow
Before: an operator whose SMTP server hangs sees the whole proxy stop responding and the pod get killed
After: the same hung SMTP server only affects the email itself
Relevant issues
Linear ticket
Resolves LIT-4992
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
uv run pytest tests/test_litellm/<your_test_file>.py -v. Leave the suites (make test-unit-*,make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more@greptileaito re-request a review after pushing changes)Delays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
Setup shared by both runs: a local banner-less SMTP server (accepts TCP on 127.0.0.1:2525, never sends the SMTP greeting), and the proxy started with
SMTP_HOST=127.0.0.1 SMTP_PORT=2525 SMTP_TLS=False [email protected] [email protected]on port 4000. The email is triggered through/health/services?service=email, which goes through the key-created email path and ends in the shared SMTP send helper, the same helper every email path (invites, budget alerts, enterprise SMTP logger) ends inBefore (807ee7f)
curl -s -m 5 http://localhost:4000/health/livelinessreturns"I'm alive!"curl -s -m 5 "http://localhost:4000/health/services?service=email" -H "Authorization: Bearer sk-1234" &starts the test email; the SMTP connect blocks the event loopcurl -s -m 5 http://localhost:4000/health/livelinesswhile the email is in flight: no response, curl exits 28 (timed out). This is the state where the kubelet kills the podAfter (75fdd7f)
curl -s -m 5 http://localhost:4000/health/livelinessreturns"I'm alive!"curl -s -m 65 "http://localhost:4000/health/services?service=email" -H "Authorization: Bearer sk-1234" &starts the same test email against the same hung SMTP servercurl -s -m 5 http://localhost:4000/health/liveliness2s into the hung SMTP send:"I'm alive!", curl exit 0curl -s -m 5 http://localhost:4000/health/liveliness12s in:"I'm alive!", curl exit 0{"status":"success","message":"Mock Email Alert sent, verify Email Alert Received"}and 30s after the send started the proxy logsAn error occurred while sending the email:Connection unexpectedly closed: timed out(TimeoutError: timed outin the traceback), matching the defaultSMTP_TIMEOUT=30Type
🐛 Bug Fix
Caveats (if any)
Low
Link to Devin session: https://app.devin.ai/sessions/d28d0fbec50b4b709720d86bca5bc416
Open in Devin Desktop: https://app.devin.ai/desktop/session/d28d0fbec50b4b709720d86bca5bc416?variant=devin
Requested by: @yassin-berriai