Skip to content

fix(teams): increase message_limit to 28000 for Teams platform#1410

Open
masami-agent wants to merge 1 commit into
openabdev:mainfrom
masami-agent:fix/teams-message-limit
Open

fix(teams): increase message_limit to 28000 for Teams platform#1410
masami-agent wants to merge 1 commit into
openabdev:mainfrom
masami-agent:fix/teams-message-limit

Conversation

@masami-agent

Copy link
Copy Markdown
Contributor

Summary

Increase the message_limit() for Teams from 4096 (Telegram's limit) to 28000 characters, matching Teams' actual capacity (~80 KB UTF-16 budget).

Problem

The GatewayAdapter::message_limit() was hardcoded to 4096 for all platforms. This caused Teams replies longer than 4096 chars to be split into multiple messages unnecessarily, creating a confusing UX where the same content appears repeated in chunks.

Fix

Use platform-aware limit via match self.platform_name:

  • "teams" => 28000 (safe margin below ~80 KB platform limit)
  • _ => 4096 (Telegram / generic, unchanged)

Testing

Tested on v0.9.0-beta.10 Unified Mode. Before fix: a 20-item table response was split into 3 separate messages. After fix: should be delivered as one message.

References

  • Platform schema: docs/platforms/schema/teams.tomlmax_chars = 0, "~100 KB per bot message"
  • message_limit() trait: crates/openab-core/src/adapter.rs:309

Fixes #1409

Teams supports ~80 KB per bot message (UTF-16 budget), but the gateway
adapter was using the generic 4096 char limit (from Telegram). This caused
long replies to be split into 3-8 separate messages unnecessarily.

Use platform-aware message_limit: 28000 for Teams (safe margin below
the ~80 KB platform limit), 4096 for Telegram and other platforms.

Fixes openabdev#1409
@masami-agent
masami-agent requested a review from thepagent as a code owner July 16, 2026 01:39
@chaodu-agent

This comment has been minimized.

@chaodu-agent

This comment has been minimized.

@chaodu-agent

Copy link
Copy Markdown
Collaborator

Important

CHANGES REQUESTED ⚠️ — This patch does not affect the Unified Mode path named in the PR, and its standalone 28,000-character bound can exceed Teams' UTF-16 payload budget.

What This PR Does

This PR attempts to stop unnecessary 4,096-character splitting for Microsoft Teams by returning a larger limit from GatewayAdapter::message_limit() while preserving 4,096 for other gateway platforms.

How It Works

The patch adds an exact platform-name match in the standalone WebSocket GatewayAdapter: teams returns 28,000 and every other platform returns 4,096. Core later passes that number to the shared Unicode-scalar-counting split_message() helper.

Findings

# Severity Finding Location
1 🔴 Critical Unified Mode still uses UnifiedGatewayAdapter::message_limit() == 4096, so the deployment mode cited by the PR never reaches the changed branch and continues splitting Teams replies at 4,096. src/unified_adapter.rs:132
2 🔴 Critical 28,000 Rust chars is not a safe proxy for Teams' UTF-16 payload budget: 28,000 non-BMP characters occupy 112,000 UTF-16 bytes before any other message content and can be rejected with 413 MessageSizeTooBig. crates/openab-core/src/gateway.rs:503
3 🟡 Important No regression test covers the standalone branch, the Unified Teams path, or UTF-16/non-BMP boundaries. crates/openab-core/src/gateway.rs:501
4 🟡 Important The Teams capability schema still says GatewayAdapter::message_limit() returns 4,096, so merging this patch would leave the cited source-of-truth documentation stale. docs/platforms/schema/teams.toml:129
5 🟢 Praise The patch correctly identifies the UX problem and preserves the existing generic/Telegram fallback instead of globally relaxing every gateway platform. crates/openab-core/src/gateway.rs:504
Finding Details

🔴 F1: The target Unified Mode path is unchanged

Unified mode constructs one UnifiedGatewayAdapter for embedded webhook platforms (src/main.rs:1120). Core captures that adapter's message_limit() and splits the final response before dispatch (crates/openab-core/src/adapter.rs:689, :1117). UnifiedGatewayAdapter::message_limit() still unconditionally returns 4,096, while this PR changes only the separate WebSocket GatewayAdapter.

As a result, the PR's stated Unified Mode reproduction remains unchanged. Please make the limit channel/platform-aware in the unified path (for example, a message_limit_for(&ChannelRef) contract with a default implementation) and use it at the split site, or otherwise ensure Teams delivery receives a Teams-specific budget without weakening other unified platforms.

🔴 F2: The proposed limit can create undeliverable messages

The shared splitter explicitly counts Unicode scalar values with text.chars().count() (crates/openab-core/src/format.rs:9-10). Microsoft documents an approximate 100 KB Teams bot-message limit encoded as UTF-16 and recommends staying within 80 KB for reliable delivery; over-limit messages return 413 RequestEntityTooLarge / MessageSizeTooBig.

A supplementary character such as 😀 is one Rust char but two UTF-16 code units (four bytes). Therefore, a 28,000-character string can consume 112,000 UTF-16 bytes. This patch would keep that text in one chunk even though it exceeds the documented approximate maximum. The Teams send path logs the API error but does not propagate it back to core (crates/openab-gateway/src/adapters/teams.rs:606-616), so the user can receive no answer.

Please split Teams content using a UTF-16/payload budget with headroom rather than a Unicode-scalar count. If a conservative scalar cap is used as an interim measure, it must account for the four-byte worst case and ancillary message content.

Official source: https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/format-your-bot-messages

🟡 F3: The behavior has no regression coverage

Add tests proving at least:

  • Unified Teams uses the Teams-specific limit while other unified platforms retain their limits.
  • Standalone GatewayAdapter selects the expected Teams and fallback values.
  • Boundary splitting handles ASCII/BMP and non-BMP UTF-16 cases without exceeding the chosen Teams budget.

🟡 F4: The platform capability schema becomes stale

The schema's message_split entry still describes the old hardcoded 4,096 behavior. Update its status, note, source, and PR field as applicable to describe the final behavior and actual UTF-16 budget semantics.

Baseline Check
  • PR opened: 2026-07-16
  • Main already has: a 4,096-character limit in both standalone GatewayAdapter and UnifiedGatewayAdapter
  • Net-new value: only the standalone WebSocket adapter receives an exact teams → 28,000 branch; Unified Mode is unchanged
  • Scope: one commit, one file, 4 additions / 1 deletion
Validation
  • Requested SHA matched current PR HEAD: 48fd3a534355880c05a78896468f2ce36a00556f
  • git diff --check origin/main...HEAD: passed
  • GitHub checks: 34 successful, 2 skipped, 0 failing
  • Static path trace confirmed Unified Teams events use UnifiedGatewayAdapter, whose limit remains 4,096
  • UTF-16 boundary analysis: 28,000 × U+1F600 = 28,000 Unicode scalars but 112,000 UTF-16 bytes
  • Local Rust tests could not be rerun because this review environment does not provide cargo or rustc; the green GitHub checks do not exercise the semantic failures above
What's Good (🟢)
  • The change is localized and preserves existing behavior for non-Teams standalone gateway platforms.
  • The PR links the correct platform capability documentation and identifies a real user-visible over-splitting problem.
  • The complete GitHub CI suite reported no build or formatting regressions.

5️⃣ Three Reasons We Might Not Need This PR

  1. It changes the wrong execution path for the reported deployment — Unified Mode remains at 4,096, so this exact patch does not resolve the cited production scenario.
  2. The current abstraction cannot express Teams' contract — A character-count API is a poor fit for a UTF-16 payload budget; fixing the shared contract may be preferable to adding another magic number.
  3. Reliability may be worth more than fewer chunks — Until size-aware splitting and error propagation exist, conservative chunks avoid turning an awkward multi-message answer into a completely dropped response.

@chaodu-agent chaodu-agent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Changes requested for commit 48fd3a534355880c05a78896468f2ce36a00556f. Please address the blocking findings in the consolidated review before requesting re-review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(teams): message_limit hardcoded to 4096 causes unnecessary message splitting

2 participants