{"record":{"id":"2a1aa99695fc392f","repo":"larksuite/cli","slug":"failed-to-read-response-body-in-security-transport","errorCode":null,"errorMessage":"failed to read response body in security transport: %w","messagePattern":"failed to read response body in security transport: %w","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/auth/transport.go","lineNumber":69,"sourceCode":"\t\t\tresp.Body.Close()\n\t\t}\n\t\treturn nil, err\n\t}\n\tif resp == nil || resp.Body == nil {\n\t\treturn resp, nil\n\t}\n\n\t// Only process JSON responses to avoid memory spikes on large files\n\tcontentType := strings.ToLower(resp.Header.Get(\"Content-Type\"))\n\tif !strings.Contains(contentType, \"application/json\") {\n\t\treturn resp, nil\n\t}\n\n\t// Read up to 64KB of the body to check for security policy errors\n\tbodyBytes, err := io.ReadAll(io.LimitReader(resp.Body, 64*1024))\n\tif err != nil {\n\t\tresp.Body.Close()\n\t\treturn nil, fmt.Errorf(\"failed to read response body in security transport: %w\", err)\n\t}\n\n\t// Restore the body so it can be read by the caller, preserving streaming capability\n\tresp.Body = struct {\n\t\tio.Reader\n\t\tio.Closer\n\t}{\n\t\tio.MultiReader(bytes.NewReader(bodyBytes), resp.Body),\n\t\tresp.Body,\n\t}\n\n\t// Try to parse it as JSON\n\tvar result map[string]interface{}\n\tif err := json.Unmarshal(bodyBytes, &result); err != nil {\n\t\treturn resp, nil\n\t}\n\n\t// 1. Try to handle as MCP (JSON-RPC) format first","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/auth/transport.go#L51-L87","documentation":"The security transport RoundTrip failed to read up to 64KB of the response body while checking for security-policy errors. It closes resp.Body and returns this wrapped error (%w, so the cause is unwrappable). The original response never reaches the caller.","triggerScenarios":"RoundTrip's io.ReadAll(io.LimitReader(resp.Body, 64*1024)) errors — connection reset or timeout while reading the head of the body, chunked-encoding corruption, or the server aborts the response.","commonSituations":"Unstable network between client and Lark gateway; TLS-terminating proxy killing long-lived connections mid-body; server-side abort on policy-violating requests.","solutions":["Retry the request — the wrapped cause (after 'security transport:') tells whether it was a reset, timeout, or EOF.","Inspect network path/proxy stability to open.feishu.cn.","If reproducible with a specific request, capture the request and test it directly to determine whether the server is aborting it.","Since the cause is %w-wrapped, use errors.Is(err, io.ErrUnexpectedEOF) etc. in caller code to branch on the exact transport failure."],"exampleFix":"// caller\nif err != nil {\n    if errors.Is(err, io.ErrUnexpectedEOF) { retry(); return }\n    return err\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":"func isSecurityTransportReadErr(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"failed to read response body in security transport\")\n}","tryCatchPattern":"resp, err := client.Do(req)\nif isSecurityTransportReadErr(err) {\n    if errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, context.DeadlineExceeded) {\n        return retry(req)\n    }\n    return err\n}","preventionTips":["Configure the HTTP client with sane timeouts and retry policy for the Lark gateway.","Stabilize the network path (avoid aggressive idle-connection-killing proxies).","Use errors.Is on the %w-wrapped cause to distinguish reset/timeout/EOF before retrying.","Keep request/response sizes modest so the 64KB security scan rarely spans resets."],"tags":["network","http","transport","go"],"backgroundTag":"http-response-read-failed","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}