chenhg5/cc-connect · error
max: send message: HTTP %d: %s
Error message
max: send message: HTTP %d: %s
What it means
The MAX send-message endpoint replied with a non-200 status that was not a retryable 'attachment not ready' condition, after exhausting the backoff retry loop. The full truncated response body is included in the error.
Source
Thrown at platform/max/max.go:1416
}
respBody, _ := io.ReadAll(io.LimitReader(resp.Body, 2048))
resp.Body.Close()
if resp.StatusCode == http.StatusOK {
return nil
}
if isAttachmentNotReady(respBody) && attempt < attachmentReadyRetries {
slog.Debug("max: attachment not ready, retrying", "attempt", attempt+1, "backoff", backoff)
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(backoff):
}
backoff *= 2
continue
}
slog.Warn("max: send message failed", "status", resp.StatusCode, "chat", chatID, "body", string(respBody))
return fmt.Errorf("max: send message: HTTP %d: %s", resp.StatusCode, respBody)
}
return fmt.Errorf("max: send message: attachment not ready after %d retries", attachmentReadyRetries)
}
func isAttachmentNotReady(body []byte) bool {
return bytes.Contains(body, []byte("attachment.not.ready")) ||
bytes.Contains(body, []byte("not.ready"))
}
func (p *Platform) getMe(ctx context.Context) (name string, id int64, err error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, p.apiBase+"/me", nil)
if err != nil {
return "", 0, err
}
p.setAuth(req)
resp, err := p.client.Do(req)
if err != nil {View on GitHub (pinned to 4000b2338a)
Solutions
- Read the status and body in the error: 401/403 means fix the bot token in config.toml or re-add the bot to the chat; 404 means the chat is gone
- For 400, log the outgoing payload and validate chat_id, text length, and keyboard structure
- Confirm the bot access token with a lightweight /me API call
- Check MAX API changelog for breaking payload changes if this started after an update
Example fix
// before
return fmt.Errorf("max: send message: HTTP %d: %s", resp.StatusCode, respBody)
// after
if resp.StatusCode == http.StatusForbidden {
return fmt.Errorf("max: send message: bot removed from chat %s (403)", chatID)
}
return fmt.Errorf("max: send message: HTTP %d: %s", resp.StatusCode, respBody) Defensive patterns
Strategy: try-catch
Validate before calling
if err := validateOutgoing(chatID, text, keyboard); err != nil { return err } // catch 400s early Try / catch
if err := send(); err != nil {
if strings.Contains(err.Error(), "HTTP 403") { reauthorizeOrNotifyBotRemoved() }
if strings.Contains(err.Error(), "HTTP 401") { alertInvalidBotToken() }
return err
} Prevention
- Validate bot token periodically (e.g. /me call) and alert before sends fail
- Truncate text to MAX message length limits before sending
- Handle 403/404 by marking the session/chat dead instead of retrying forever
When it happens
Trigger: 400 for a malformed payload (bad chat_id, oversized text, invalid keyboard JSON), 401/403 for bad or revoked bot token, 404 for unknown chat, or persistent 5xx across all retries.
Common situations: Bot removed from the chat (403) or chat deleted (404); token rotated in MAX without updating config.toml; message text exceeding MAX length limits; permanent API-side rejection of a field the platform sends.
Related errors
- cloud_web: send HTTP %d: %s
- max: send message: %w
- max: send message: attachment not ready after %d retries
- too many redirects
- range chunk retries exhausted
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/9b36ae9cbe58e80f.
Report an issue: GitHub.