From 7634bb783484d5c64547c5c91c4607cd6e44586c Mon Sep 17 00:00:00 2001 From: viettranx Date: Fri, 20 Mar 2026 18:24:00 +0700 Subject: [PATCH] fix(telegram): use typed error checks for 5xx detection in retry/dedup logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace loose string matching ("502", "503", "504") with telegoapi.Error type assertion — prevents false positives from error messages that happen to contain these substrings. Also classifies 5xx as post-connect errors since the server received the request. --- internal/channels/telegram/send.go | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/internal/channels/telegram/send.go b/internal/channels/telegram/send.go index b6816007..628db4ef 100644 --- a/internal/channels/telegram/send.go +++ b/internal/channels/telegram/send.go @@ -2,6 +2,7 @@ package telegram import ( "context" + "errors" "fmt" "html" "log/slog" @@ -11,6 +12,7 @@ import ( "time" "github.com/mymmrac/telego" + "github.com/mymmrac/telego/telegoapi" tu "github.com/mymmrac/telego/telegoutil" "github.com/nextlevelbuilder/goclaw/internal/bus" @@ -38,31 +40,39 @@ func stripHTML(s string) string { return html.UnescapeString(htmlTagRe.ReplaceAllString(s, "")) } -// isRetryableNetworkErr checks if a Telegram API error is a transient network error worth retrying. -// Includes post-connect network stalls and 5xx server errors. +// isRetryableNetworkErr checks if a Telegram API error is a transient network/server error +// worth retrying. Covers transport-level failures and Telegram 5xx server errors. func isRetryableNetworkErr(err error) bool { if err == nil { return false } + // Check for Telegram API 5xx errors via typed error. + var apiErr *telegoapi.Error + if errors.As(err, &apiErr) && apiErr.ErrorCode >= 500 { + return true + } + // Transport-level errors (timeout, reset, DNS, etc.) s := err.Error() return strings.Contains(s, "timeout") || strings.Contains(s, "connection reset") || strings.Contains(s, "broken pipe") || strings.Contains(s, "EOF") || - strings.Contains(s, "lookup") || // DNS resolution failure - strings.Contains(s, "502") || // Bad Gateway - strings.Contains(s, "503") || // Service Unavailable - strings.Contains(s, "504") // Gateway Timeout + strings.Contains(s, "lookup") // DNS resolution failure } -// isPostConnectNetworkErr check if the error likely occurred AFTER reaching the server -// (timeout, connection reset, EOF) vs before (DNS lookup failure). +// isPostConnectNetworkErr checks if the error likely occurred AFTER reaching the server +// (timeout, connection reset, EOF) vs before (DNS lookup failure, connection refused). +// Used to decide if a request may have landed despite the error. func isPostConnectNetworkErr(err error) bool { if err == nil { return false } + // Telegram 5xx means the server received the request. + var apiErr *telegoapi.Error + if errors.As(err, &apiErr) && apiErr.ErrorCode >= 500 { + return true + } s := err.Error() - // Exclude "lookup" (DNS) and "connection refused" as they are safe pre-connect errors. return (strings.Contains(s, "timeout") || strings.Contains(s, "connection reset") || strings.Contains(s, "broken pipe") ||