send_email from Day 7 sends the email or raises an exception. If your Monday pipeline runs at 8 AM and Gmail loses its OAuth token overnight, what happens to the rest of the pipeline?
send_email calls GMAIL_SEND_EMAIL directly. If the token is missing, the action raises an exception and the whole script crashes before creating the Calendar event or the task list. The pipeline is all-or-nothing.
safe_send wraps the call in try/except and catches two failure modes: AUTH_REQUIRED in the error string (expired token), and any other exception (network timeout, rate limit). Instead of crashing, it returns a status dict — {"status": "sent"} on success, {"status": "auth_required"} or {"status": "error", "message": str(e)} on failure:
try:
result = toolset.execute_action(
Action.GMAIL_SEND_EMAIL,
{"to": to, "subject": subject, "body": body}
)
return {"status": "sent", "result": result}
except Exception as e:
if "AUTH_REQUIRED" in str(e):
return {"status": "auth_required", "message": str(e)}
return {"status": "error", "message": str(e)}Why check "AUTH_REQUIRED" in str(e) instead of catching a specific exception class?
Composio wraps all action errors in a generic Exception — there is no AuthRequired subclass to catch. Checking the string is the correct pattern here. If the error string contains AUTH_REQUIRED, you know the connection needs to be re-authorised; otherwise you have a transient failure. Both return structured dicts so the caller can log and continue.
So run_workflow on Day 28 calls safe_send instead of send_email, checks the returned status, and continues to the Calendar and Tasks steps even if Gmail is down. The pipeline degrades gracefully instead of crashing.
Exactly. "Partial success" beats "total failure" every time a co-author is waiting for the Monday digest.
And I can add the same wrapper to any action — Calendar, Sheets, LinkedIn — by swapping the action enum. One pattern, seven apps.
The pattern generalises. Swap the action enum to wrap any Composio call the same way:
try:
result = toolset.execute_action(
Action.GOOGLECALENDAR_CREATE_EVENT,
{"calendar_id": cal_id, "summary": title}
)
return {"status": "created", "result": result}
except Exception as e:
if "AUTH_REQUIRED" in str(e):
return {"status": "auth_required", "message": str(e)}
return {"status": "error", "message": str(e)}Keep the wrapper thin — catch, classify, return. Leave retries and alerting to the caller.
try:
result = toolset.execute_action(Action.GMAIL_SEND_EMAIL, {...})
return {"status": "sent", "result": result}
except Exception as e:
if "AUTH_REQUIRED" in str(e):
return {"status": "auth_required", "message": str(e)}
return {"status": "error", "message": str(e)}| Status | Meaning |
|---|---|
"sent" | Email delivered |
"auth_required" | OAuth token expired — reconnect Gmail |
"error" | Network or rate-limit failure |
In a multi-step pipeline, a raise stops all downstream steps. A structured status dict lets the caller log the failure and continue with Calendar and Tasks.
send_email from Day 7 sends the email or raises an exception. If your Monday pipeline runs at 8 AM and Gmail loses its OAuth token overnight, what happens to the rest of the pipeline?
send_email calls GMAIL_SEND_EMAIL directly. If the token is missing, the action raises an exception and the whole script crashes before creating the Calendar event or the task list. The pipeline is all-or-nothing.
safe_send wraps the call in try/except and catches two failure modes: AUTH_REQUIRED in the error string (expired token), and any other exception (network timeout, rate limit). Instead of crashing, it returns a status dict — {"status": "sent"} on success, {"status": "auth_required"} or {"status": "error", "message": str(e)} on failure:
try:
result = toolset.execute_action(
Action.GMAIL_SEND_EMAIL,
{"to": to, "subject": subject, "body": body}
)
return {"status": "sent", "result": result}
except Exception as e:
if "AUTH_REQUIRED" in str(e):
return {"status": "auth_required", "message": str(e)}
return {"status": "error", "message": str(e)}Why check "AUTH_REQUIRED" in str(e) instead of catching a specific exception class?
Composio wraps all action errors in a generic Exception — there is no AuthRequired subclass to catch. Checking the string is the correct pattern here. If the error string contains AUTH_REQUIRED, you know the connection needs to be re-authorised; otherwise you have a transient failure. Both return structured dicts so the caller can log and continue.
So run_workflow on Day 28 calls safe_send instead of send_email, checks the returned status, and continues to the Calendar and Tasks steps even if Gmail is down. The pipeline degrades gracefully instead of crashing.
Exactly. "Partial success" beats "total failure" every time a co-author is waiting for the Monday digest.
And I can add the same wrapper to any action — Calendar, Sheets, LinkedIn — by swapping the action enum. One pattern, seven apps.
The pattern generalises. Swap the action enum to wrap any Composio call the same way:
try:
result = toolset.execute_action(
Action.GOOGLECALENDAR_CREATE_EVENT,
{"calendar_id": cal_id, "summary": title}
)
return {"status": "created", "result": result}
except Exception as e:
if "AUTH_REQUIRED" in str(e):
return {"status": "auth_required", "message": str(e)}
return {"status": "error", "message": str(e)}Keep the wrapper thin — catch, classify, return. Leave retries and alerting to the caller.
try:
result = toolset.execute_action(Action.GMAIL_SEND_EMAIL, {...})
return {"status": "sent", "result": result}
except Exception as e:
if "AUTH_REQUIRED" in str(e):
return {"status": "auth_required", "message": str(e)}
return {"status": "error", "message": str(e)}| Status | Meaning |
|---|---|
"sent" | Email delivered |
"auth_required" | OAuth token expired — reconnect Gmail |
"error" | Network or rate-limit failure |
In a multi-step pipeline, a raise stops all downstream steps. A structured status dict lets the caller log the failure and continue with Calendar and Tasks.
Create a free account to get started. Paid plans unlock all tracks.