{"record":{"id":"9b36ae9cbe58e80f","repo":"chenhg5/cc-connect","slug":"max-send-message-http-d-s","errorCode":null,"errorMessage":"max: send message: HTTP %d: %s","messagePattern":"max: send message: HTTP (.+?): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/max/max.go","lineNumber":1416,"sourceCode":"\t\t}\n\t\trespBody, _ := io.ReadAll(io.LimitReader(resp.Body, 2048))\n\t\tresp.Body.Close()\n\n\t\tif resp.StatusCode == http.StatusOK {\n\t\t\treturn nil\n\t\t}\n\t\tif isAttachmentNotReady(respBody) && attempt < attachmentReadyRetries {\n\t\t\tslog.Debug(\"max: attachment not ready, retrying\", \"attempt\", attempt+1, \"backoff\", backoff)\n\t\t\tselect {\n\t\t\tcase <-ctx.Done():\n\t\t\t\treturn ctx.Err()\n\t\t\tcase <-time.After(backoff):\n\t\t\t}\n\t\t\tbackoff *= 2\n\t\t\tcontinue\n\t\t}\n\t\tslog.Warn(\"max: send message failed\", \"status\", resp.StatusCode, \"chat\", chatID, \"body\", string(respBody))\n\t\treturn fmt.Errorf(\"max: send message: HTTP %d: %s\", resp.StatusCode, respBody)\n\t}\n\treturn fmt.Errorf(\"max: send message: attachment not ready after %d retries\", attachmentReadyRetries)\n}\n\nfunc isAttachmentNotReady(body []byte) bool {\n\treturn bytes.Contains(body, []byte(\"attachment.not.ready\")) ||\n\t\tbytes.Contains(body, []byte(\"not.ready\"))\n}\n\nfunc (p *Platform) getMe(ctx context.Context) (name string, id int64, err error) {\n\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, p.apiBase+\"/me\", nil)\n\tif err != nil {\n\t\treturn \"\", 0, err\n\t}\n\tp.setAuth(req)\n\n\tresp, err := p.client.Do(req)\n\tif err != nil {","sourceCodeStart":1398,"sourceCodeEnd":1434,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/max/max.go#L1398-L1434","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nreturn fmt.Errorf(\"max: send message: HTTP %d: %s\", resp.StatusCode, respBody)\n// after\nif resp.StatusCode == http.StatusForbidden {\n\treturn fmt.Errorf(\"max: send message: bot removed from chat %s (403)\", chatID)\n}\nreturn fmt.Errorf(\"max: send message: HTTP %d: %s\", resp.StatusCode, respBody)","handlingStrategy":"try-catch","validationCode":"if err := validateOutgoing(chatID, text, keyboard); err != nil { return err } // catch 400s early","typeGuard":null,"tryCatchPattern":"if err := send(); err != nil {\n\tif strings.Contains(err.Error(), \"HTTP 403\") { reauthorizeOrNotifyBotRemoved() }\n\tif strings.Contains(err.Error(), \"HTTP 401\") { alertInvalidBotToken() }\n\treturn err\n}","preventionTips":["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"],"tags":["http","send","bot-api","retries"],"backgroundTag":"http-non-2xx-response","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}