{"record":{"id":"9845120a8238aae6","repo":"chenhg5/cc-connect","slug":"max-edit-message-w","errorCode":null,"errorMessage":"max: edit message: %w","messagePattern":"max: edit message: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/max/max.go","lineNumber":519,"sourceCode":"\t}\n\tbody := maxSendBody{Text: content, Format: \"markdown\"}\n\tdata, err := json.Marshal(body)\n\tif err != nil {\n\t\treturn err\n\t}\n\treq, err := http.NewRequestWithContext(ctx, http.MethodPut, p.apiBase+\"/messages\", bytes.NewReader(data))\n\tif err != nil {\n\t\treturn err\n\t}\n\tp.setAuth(req)\n\tq := req.URL.Query()\n\tq.Set(\"message_id\", rctx.messageID)\n\treq.URL.RawQuery = q.Encode()\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := p.client.Do(req)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"max: edit message: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {\n\t\trespBody, _ := io.ReadAll(io.LimitReader(resp.Body, 512))\n\t\treturn fmt.Errorf(\"max: edit message: HTTP %d: %s\", resp.StatusCode, respBody)\n\t}\n\treturn nil\n}\n\n// uploadAttachment performs the two-step MAX upload: request an upload URL from\n// /uploads?type=<kind>, then POST the binary as multipart/form-data field \"data\"\n// to that URL. Returns the token to embed in a subsequent /messages attachment.\nfunc (p *Platform) uploadAttachment(ctx context.Context, kind string, data []byte, filename string) (string, error) {\n\tif len(data) == 0 {\n\t\treturn \"\", fmt.Errorf(\"empty attachment data\")\n\t}\n\t// Use a 5-minute context AND a dedicated http.Client with a matching Timeout.\n\t// p.client has a 35 s Timeout which fires independently of the context deadline","sourceCodeStart":501,"sourceCodeEnd":537,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/max/max.go#L501-L537","documentation":"This wraps a transport-level failure of the PUT /messages?message_id= request used to edit a MAX message: p.client.Do returned an error before an HTTP response could be read. The wrapped cause is typically a timeout (p.client has a 35s timeout), connection refused/reset, DNS failure, or request-context cancellation — i.e. the edit request never completed, so the message text is unchanged.","triggerScenarios":"Calling UpdateMessage when: (1) the 35s http.Client timeout fires on a slow/hung connection; (2) the caller's context is canceled mid-request; (3) TCP connection to botapi.max.ru fails (connection refused, reset, TLS handshake failure); (4) DNS resolution fails; (5) a proxy intercepts and breaks the connection.","commonSituations":"Streaming-style frequent edits causing many rapid PUTs over a flaky mobile link; daemon running behind a corporate proxy that drops keep-alive connections; host with intermittent DNS; the caller cancels the context (user sent a new command) while the edit is in flight.","solutions":["Check connectivity from the host to the MAX API endpoint (curl the API base) to distinguish network outage from code issues.","Implement retry with backoff for transient transport errors (errors.Is(err, context.DeadlineExceeded), net.Error Timeout(), connection reset) — edits are idempotent per message_id.","Verify the outbound context passed to UpdateMessage isn't canceled too early (e.g. a short request timeout upstream).","Check proxy/firewall settings (HTTPS_PROXY, TLS interception) on the machine running cc-connect.","Throttle rapid successive edits of the same message to avoid hammering the connection."],"exampleFix":"// before\nerr := platform.UpdateMessage(ctx, rctx, newText)\nif err != nil { return err }\n\n// after\nerr := platform.UpdateMessage(ctx, rctx, newText)\nif err != nil {\n    var ne net.Error\n    if errors.As(err, &ne) && ne.Timeout() || errors.Is(err, context.DeadlineExceeded) {\n        time.Sleep(500 * time.Millisecond)\n        return platform.UpdateMessage(ctx, rctx, newText) // retry transient failure\n    }\n    return fmt.Errorf(\"max: edit message: %w\", err)\n}","handlingStrategy":"retry","validationCode":"select {\ncase <-ctx.Done():\n    return ctx.Err() // don't even attempt if already canceled\ndefault:\n}","typeGuard":null,"tryCatchPattern":"err := platform.UpdateMessage(ctx, replyCtx, content)\nif err != nil {\n    var ne net.Error\n    if errors.As(err, &ne) && ne.Timeout() ||\n        errors.Is(err, syscall.ECONNRESET) || errors.Is(err, syscall.ECONNREFUSED) {\n        time.Sleep(backoff) // then retry once or twice\n        return platform.UpdateMessage(ctx, replyCtx, content)\n    }\n    return err\n}","preventionTips":["Add bounded retry with exponential backoff for transport errors — edits are idempotent for a given message_id.","Keep the 35s client timeout in mind; pass a caller context with a slightly larger deadline so cancellation is deliberate, not accidental.","Monitor outbound connectivity/proxy health to the MAX API host; alert on repeated edit failures.","Rate-limit successive edits of the same message to reduce connection churn.","Treat this as transient when message text correctness matters: re-read state and re-apply the edit after recovery."],"tags":["go","network","http-client","max-platform","message-editing"],"backgroundTag":"http-request-failed","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"}