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

  1. Retry once — a fresh request usually succeeds because the failure is mid-stream.
  2. Check intermediary proxy timeouts if it recurs in one environment only.
  3. 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

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


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/7bd699a257188c3e. Report an issue: GitHub.