improve: propagate send errors back to agent for retry on SMTP failures
linuztx committed
Mar 16, 2026 at 09:08 UTC
3dcffe662c36f8702c8968909ecb46ad0323742a
5 files changed
+32
-12
plugins/_email_integration/extensions/python/process_chain_end/_55_email_reply.py
+11
-2
@@ -3,7 +3,7 @@
3
import asyncio
4
from helpers.extension import Extension
5
from helpers.print_style import PrintStyle
6
-from agent import AgentContext, LoopData
6
+from agent import AgentContext, LoopData, UserMessage
7
from plugins._email_integration.helpers.dispatcher import CTX_EMAIL_HANDLER, CTX_EMAIL_ATTACHMENTS
8
9
@@ -30,7 +30,9 @@ class EmailAutoReply(Extension):
30
self, context: AgentContext, response_text: str, attachments: list[str],
31
):
32
from plugins._email_integration.helpers.handler import send_email_reply
33
- await send_email_reply(context, response_text, attachments)
33
+ error = await send_email_reply(context, response_text, attachments)
34
+ if error:
35
+ _notify_agent_of_failure(context, error)
36
37
38
# ------------------------------------------------------------------
@@ -46,3 +48,10 @@ def _extract_last_response(context: AgentContext) -> str:
48
if item.type == "response":
49
return item.content or ""
50
return ""
51
+
52
+
53
+def _notify_agent_of_failure(context: AgentContext, error: str):
54
+ from plugins._email_integration.helpers.handler import _read_fw
55
+ msg = _read_fw("fw.email.send_failed.md", error=error)
56
+ context.log.log(type="error", heading="Email send failed", content=error)
57
+ context.communicate(UserMessage(message="", system_message=[msg]))
plugins/_email_integration/helpers/handler.py
+4
-5
@@ -333,15 +333,14 @@ async def send_email_reply(
333
context: AgentContext,
334
response_text: str,
335
attachments: list[str] | None = None,
336
-):
336
+) -> str | None:
337
handler_name = context.data.get(disp.CTX_EMAIL_HANDLER)
338
if not handler_name:
339
- return
339
+ return "No email handler configured"
340
341
cfg = _get_handler_config(handler_name)
342
if not cfg:
343
- PrintStyle.error(f"Email: handler config not found for '{handler_name}'")
344
- return
343
+ return f"Handler config not found for '{handler_name}'"
344
345
sender = context.data.get(disp.CTX_EMAIL_SENDER, "")
346
original_subject = context.data.get(disp.CTX_EMAIL_SUBJECT, "")
@@ -361,7 +360,7 @@ async def send_email_reply(
360
# Read attachment files via RFC (they live in the execution runtime)
361
attachment_data = await _read_attachments_via_rfc(attachments)
362
364
- await send_reply(
363
+ return await send_reply(
364
config=smtp_cfg,
365
to=sender,
366
subject=subject,
plugins/_email_integration/helpers/smtp_client.py
+5
-4
@@ -41,7 +41,7 @@ async def send_reply(
41
in_reply_to: str = "",
42
references: str = "",
43
attachments: list[tuple[str, bytes]] | None = None,
44
-) -> bool:
44
+) -> str | None:
45
loop = asyncio.get_event_loop()
46
47
def _sync_send():
@@ -82,7 +82,8 @@ async def send_reply(
82
try:
83
await loop.run_in_executor(None, _sync_send)
84
PrintStyle.success(f"Email sent to {to}: {subject}")
85
- return True
85
+ return None
86
except Exception as e:
87
- PrintStyle.error(f"Email send failed: {format_error(e)}")
88
- return False
87
+ error = format_error(e)
88
+ PrintStyle.error(f"Email send failed: {error}")
89
+ return error
plugins/_email_integration/prompts/fw.email.send_failed.md
new
+3
@@ -0,0 +1,3 @@
1
+email send failed: {{error}}
2
+previous response was NOT delivered to user
3
+retry without problematic content or inform user via alternative means
plugins/_email_integration/tools/email_update.py
+9
-1
@@ -28,7 +28,15 @@ class EmailUpdate(Tool):
28
attachments = list(self.args.get("attachments", []))
29
30
from plugins._email_integration.helpers.handler import send_email_reply
31
- await send_email_reply(self.agent.context, text, attachments or None)
31
+ error = await send_email_reply(self.agent.context, text, attachments or None)
32
+
33
+ if error:
34
+ return Response(
35
+ message=self.agent.read_prompt(
36
+ "fw.email.update_error.md", error=error,
37
+ ),
38
+ break_loop=False,
39
+ )
40
41
return Response(
42
message=self.agent.read_prompt("fw.email.update_ok.md"),