diff --git a/apps/api/src/services/telegram_gateway.py b/apps/api/src/services/telegram_gateway.py
index 32b7f212..9f28f5de 100644
--- a/apps/api/src/services/telegram_gateway.py
+++ b/apps/api/src/services/telegram_gateway.py
@@ -2100,26 +2100,44 @@ class TelegramGateway:
})
return
- _lines = [f"📊 完整 Drift Diff — {html.escape(report_id)}"]
- _lines.append(f"Namespace: {html.escape(_rpt.namespace)}")
- _lines.append(f"HIGH×{_rpt.high_count} MEDIUM×{_rpt.medium_count} INFO×{_rpt.info_count}")
- _lines.append("━" * 20)
- for i, _item in enumerate(_rpt.items[:50], 1):
+ # 2026-04-19 ogt + Claude Opus 4.7: 修 HTTP 400 真因
+ # 原邏輯: _full[:3950] 切在 HTML tag/entity 中間 → Telegram parse_mode HTML 拒絕
+ # 修法: item-by-item 累計長度,超過 3800 就停,確保完整 HTML 結構
+ # (3800 留 250 buffer 給 header + 截斷提示)
+ _MAX_LEN = 3800
+
+ _header = [
+ f"📊 完整 Drift Diff — {html.escape(report_id)}",
+ f"Namespace: {html.escape(_rpt.namespace)}",
+ f"HIGH×{_rpt.high_count} MEDIUM×{_rpt.medium_count} INFO×{_rpt.info_count}",
+ "━" * 20,
+ ]
+ _lines = list(_header)
+ _used_len = sum(len(s) + 1 for s in _header)
+ _shown = 0
+
+ for _item in _rpt.items:
_level = getattr(_item.drift_level, "value", str(_item.drift_level))
_emoji = "🔴" if _level == "high" else ("🟡" if _level == "medium" else "⚪")
_field = (_item.field_path or "")[:80]
_git = str(_item.git_value)[:40] if _item.git_value is not None else "(未設)"
_k8s = str(_item.actual_value)[:40] if _item.actual_value is not None else "(未設)"
- _lines.append(f"{_emoji} {html.escape(_field)}")
- _lines.append(f" Git: {html.escape(_git)}")
- _lines.append(f" K8s: {html.escape(_k8s)}")
- if len(_rpt.items) > 50:
- _lines.append(f"… 還有 {len(_rpt.items) - 50} 項未顯示")
+ _block = (
+ f"{_emoji} {html.escape(_field)}\n"
+ f" Git: {html.escape(_git)}\n"
+ f" K8s: {html.escape(_k8s)}"
+ )
+ if _used_len + len(_block) + 1 > _MAX_LEN:
+ break
+ _lines.append(_block)
+ _used_len += len(_block) + 1
+ _shown += 1
+
+ _remaining = len(_rpt.items) - _shown
+ if _remaining > 0:
+ _lines.append(f"… 還有 {_remaining} 項未顯示")
_full = "\n".join(_lines)
- # Telegram 訊息上限 4096 字元
- if len(_full) > 4000:
- _full = _full[:3950] + "\n… (截斷)"
await self._send_request("sendMessage", {
"chat_id": settings.OPENCLAW_TG_CHAT_ID,