sipeed/picoclaw · error
failed to read response: %w
Error message
failed to read response: %w
What it means
Thrown when io.ReadAll(resp.Body) fails in ElevenLabsTranscriber.Transcribe — the HTTP status line was received and the body started streaming, but the read aborted. Typical causes: connection reset by the peer or an intermediary mid-body, keep-alive socket closed, or ctx cancelled while the body streams. The defer resp.Body.Close() runs afterwards, so no fd leak.
Source
Thrown at pkg/audio/asr/elevenlabs_transcriber.go:117
req.Header.Set("Xi-Api-Key", t.apiKey)
logger.DebugCF("voice", "Sending transcription request to ElevenLabs API", map[string]any{
"url": url,
"request_size_bytes": requestBody.Len(),
"file_size_bytes": fileInfo.Size(),
})
resp, err := t.httpClient.Do(req)
if err != nil {
logger.ErrorCF("voice", "Failed to send request", map[string]any{"error": err})
return nil, fmt.Errorf("failed to send request: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
logger.ErrorCF("voice", "Failed to read response", map[string]any{"error": err})
return nil, fmt.Errorf("failed to read response: %w", err)
}
if resp.StatusCode != http.StatusOK {
logger.ErrorCF("voice", "ElevenLabs API error", map[string]any{
"status_code": resp.StatusCode,
"response": string(body),
})
return nil, fmt.Errorf("ElevenLabs API error (status %d): %s", resp.StatusCode, string(body))
}
logger.DebugCF("voice", "Received response from ElevenLabs API", map[string]any{
"status_code": resp.StatusCode,
"response_size_bytes": len(body),
})
var result TranscriptionResponse
if err := json.Unmarshal(body, &result); err != nil {
logger.ErrorCF("voice", "Failed to unmarshal response", map[string]any{"error": err})View on GitHub (pinned to 49183d7e8d)
Solutions
- Retry once — a fresh request usually succeeds because the failure is mid-stream.
- Check intermediary proxy timeouts if it recurs in one environment only.
- Propagate ctx instead of context.Background() so cancellation is intentional, not accidental.
Defensive patterns
Strategy: retry
Type guard
func isMidBodyDrop(err error) bool {
var netErr net.Error
return errors.As(err, &netErr) || errors.Is(err, io.ErrUnexpectedEOF)
} Try / catch
resp, err := el.Transcribe(ctx, path)
if err != nil && isMidBodyDrop(err) {
// mid-response drop: idempotent POST of the same file, safe to retry once
resp, err = el.Transcribe(ctx, path)
} Prevention
- Raise intermediary proxy body/read timeouts if the path crosses nginx/Envoy.
- Retry once on unexpected EOF — the request already succeeded server-side.
When it happens
Trigger: ElevenLabs/CDN edge drops the connection mid-response; a proxy enforces a shorter body timeout than the client; ctx is cancelled while a large JSON transcript streams back.
Common situations: Flaky mobile/edge networks; nginx/Envoy between client and API with aggressive proxy_read_timeout; long audio jobs whose transcript response is large.
Related errors
- failed to send request: %w
- failed to write tts audio: %w
- after %d retries: %w
- LLM call failed after retries: %w
- transcription request failed: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/7bd699a257188c3e.
Report an issue: GitHub.