{"record":{"id":"2702015c35cdb785","repo":"sipeed/picoclaw","slug":"failed-to-read-response-w-270201","errorCode":null,"errorMessage":"failed to read response: %w","messagePattern":"failed to read response: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/audio/tts/mimo_tts.go","lineNumber":130,"sourceCode":"\t}\n\n\treq, err := http.NewRequestWithContext(ctx, \"POST\", t.apiBase, bytes.NewReader(jsonData))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create request: %w\", err)\n\t}\n\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\treq.Header.Set(\"Api-Key\", t.apiKey)\n\n\tresp, err := t.httpClient.Do(req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to send request: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read response: %w\", err)\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"API error (status %d): %s\", resp.StatusCode, string(body))\n\t}\n\n\tvar payload struct {\n\t\tChoices []struct {\n\t\t\tMessage struct {\n\t\t\t\tAudio struct {\n\t\t\t\t\tData string `json:\"data\"`\n\t\t\t\t} `json:\"audio\"`\n\t\t\t} `json:\"message\"`\n\t\t} `json:\"choices\"`\n\t}\n\n\terr = json.Unmarshal(body, &payload)\n\tif err != nil {","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/audio/tts/mimo_tts.go#L112-L148","documentation":"Returned by MimoTTSProvider.Synthesize when io.ReadAll fails while draining resp.Body. The request already succeeded at the transport level; the failure is a mid-body connection reset, the server closing early, context cancellation during the read, or an enormous base64 response exhausting memory.","triggerScenarios":"Connection dropped between response headers and the final chunk (proxy/idle timeouts); server closes keep-alive early; caller cancels ctx mid-read; multi-MB audio payload for very long text read in one shot.","commonSituations":"Flaky mobile networks; aggressive corporate proxies; long synthesis inputs producing large base64 bodies.","solutions":["Retry once — partial reads are usually transient","Shorten the input text to reduce response size","Verify proxy and read-idle timeouts on the network path","Keep the context alive for the entire Synthesize call"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":"func isBodyReadFailure(err error) bool {\n    var ne net.Error\n    return errors.Is(err, io.ErrUnexpectedEOF) || errors.As(err, &ne)\n}","tryCatchPattern":"if err != nil && isBodyReadFailure(err) {\n    // response broke mid-read: safe to retry — the request body is rebuilt from bytes each attempt\n}","preventionTips":["Keep the caller context alive until Synthesize returns","Shorten inputs to shrink the base64 response body","Check proxy idle/read timeouts on the network path"],"tags":["network","io","tts","mimo"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}