From a715f672f18f7ff65e7277d5720d06163cb2737a Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 20 Mar 2026 22:35:53 +0000 Subject: [PATCH 1/2] Fix Teams 413 error: add charset=utf-8 to Content-Type header and retry on 413 Co-Authored-By: Michael Myaskovsky --- .../messaging_integrations/teams_webhook.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/elementary/messages/messaging_integrations/teams_webhook.py b/elementary/messages/messaging_integrations/teams_webhook.py index 533fe82a1..ff6c8aa7c 100644 --- a/elementary/messages/messaging_integrations/teams_webhook.py +++ b/elementary/messages/messaging_integrations/teams_webhook.py @@ -107,9 +107,26 @@ def send_adaptive_card(webhook_url: str, card: dict) -> requests.Response: response = requests.post( webhook_url, json=payload, - headers={"Content-Type": "application/json"}, + headers={"Content-Type": "application/json; charset=utf-8"}, ) response.raise_for_status() + if ( + response.status_code == HTTPStatus.OK + and len(response.text) > 1 + and "413" in response.text + ): + logger.warning( + "Teams webhook returned 413 in response body (payload size issue), " + "retrying with minimal card" + ) + minimal = _minimal_card(card) + payload = _build_payload(minimal) + response = requests.post( + webhook_url, + json=payload, + headers={"Content-Type": "application/json; charset=utf-8"}, + ) + response.raise_for_status() if response.status_code == HTTPStatus.ACCEPTED: logger.debug( f"Got {HTTPStatus.ACCEPTED} response from Teams webhook, assuming success" From 49a3dbd7c639747835c6a10943d2567647453d88 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 20 Mar 2026 22:41:01 +0000 Subject: [PATCH 2/2] Address review: truly minimal fallback card, add request timeouts Co-Authored-By: Michael Myaskovsky --- elementary/messages/messaging_integrations/teams_webhook.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/elementary/messages/messaging_integrations/teams_webhook.py b/elementary/messages/messaging_integrations/teams_webhook.py index ff6c8aa7c..a7d031c25 100644 --- a/elementary/messages/messaging_integrations/teams_webhook.py +++ b/elementary/messages/messaging_integrations/teams_webhook.py @@ -27,6 +27,7 @@ Channel: TypeAlias = Optional[str] ONE_SECOND = 1 TEAMS_PAYLOAD_SIZE_LIMIT = 27 * 1024 +REQUEST_TIMEOUT_SECONDS = (3.05, 10) class TeamsWebhookHttpError(MessagingIntegrationError): @@ -62,7 +63,8 @@ def _truncation_notice_item() -> Dict[str, Any]: def _minimal_card(card: dict) -> dict: return { - **card, + "type": "AdaptiveCard", + "version": card.get("version", "1.5"), "body": [ { "type": "TextBlock", @@ -108,6 +110,7 @@ def send_adaptive_card(webhook_url: str, card: dict) -> requests.Response: webhook_url, json=payload, headers={"Content-Type": "application/json; charset=utf-8"}, + timeout=REQUEST_TIMEOUT_SECONDS, ) response.raise_for_status() if ( @@ -125,6 +128,7 @@ def send_adaptive_card(webhook_url: str, card: dict) -> requests.Response: webhook_url, json=payload, headers={"Content-Type": "application/json; charset=utf-8"}, + timeout=REQUEST_TIMEOUT_SECONDS, ) response.raise_for_status() if response.status_code == HTTPStatus.ACCEPTED: