sipeed/picoclaw · error
failed to send request: %w
Error message
failed to send request: %w
What it means
Thrown when t.httpClient.Do(req) fails in ElevenLabsTranscriber.Transcribe. The client has a 120-second total timeout, so this wraps transport errors: DNS resolution failure, connection refused/reset, TLS handshake errors, context cancellation/deadline, or the 120s timeout firing on a slow upload. The error wraps *url.Error which itself wraps the cause and can be unwrapped with errors.Is/As.
Source
Thrown at pkg/audio/asr/elevenlabs_transcriber.go:110
req, err := http.NewRequestWithContext(ctx, "POST", url, &requestBody)
if err != nil {
logger.ErrorCF("voice", "Failed to create request", map[string]any{"error": err})
return nil, fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("Content-Type", writer.FormDataContentType())
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{View on GitHub (pinned to 49183d7e8d)
Solutions
- Unwrap the error: net.Error.Timeout() distinguishes timeout from refusal; errors.Is(err, context.Canceled) detects cancellation.
- Retry with exponential backoff on transient transport errors (reset, timeout, DNS hiccup).
- Set HTTPS_PROXY/HTTP_PROXY or fix DNS in the deployment environment.
- For big files on slow links, compress or downsample audio first (e.g. 16kHz mono Opus) so upload fits the 120s window.
- Check ctx deadlines set by callers — a 60s caller deadline will surface here as context.DeadlineExceeded.
Example fix
// before
resp, err := transcriber.Transcribe(ctx, path)
if err != nil { return err }
// after
resp, err := transcriber.Transcribe(ctx, path)
if err != nil {
var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() {
return retryWithBackoff(ctx, func() (*asr.TranscriptionResponse, error) {
return transcriber.Transcribe(ctx, path)
})
}
if errors.Is(err, context.Canceled) { return err }
return err
} Defensive patterns
Strategy: retry
Validate before calling
// Optional preflight (cheap, catches DNS/proxy/egress issues early):
if err := preflightEndpoint(ctx, "https://api.elevenlabs.io", 2*time.Second); err != nil {
return fmt.Errorf("cannot reach ElevenLabs: %w", err)
} Type guard
func isTransientTransport(err error) bool {
var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() { return true }
if errors.Is(err, syscall.ECONNRESET) || errors.Is(err, syscall.ECONNREFUSED) { return true }
if errors.Is(err, context.DeadlineExceeded) { return true }
return false
} Try / catch
var lastErr error
for attempt := 0; attempt < 3; attempt++ {
resp, err := el.Transcribe(ctx, path)
if err == nil { return resp, nil }
lastErr = err
if errors.Is(err, context.Canceled) || !isTransientTransport(err) { break }
time.Sleep(time.Duration(1<<attempt) * time.Second)
}
return nil, fmt.Errorf("ElevenLabs transcription failed: %w", lastErr) Prevention
- Set HTTPS_PROXY in corporate networks; verify egress to api.elevenlabs.io:443.
- Keep individual audio uploads well under the 120s client timeout (compress/downsample).
- Give the caller ctx a deadline larger than 120s so the client timeout fires first and is distinguishable.
When it happens
Trigger: No network egress to api.elevenlabs.io (air-gapped or firewall); DNS server unreachable; corporate proxy required but not configured (HTTP_PROXY/HTTPS_PROXY); self-signed apiBase certificate failing verification; audio upload slower than 120s (very large files on slow links); ctx cancelled before/during the call.
Common situations: Containers without proxy env vars inside corporate networks; transient ISP/DNS outages; huge WAV uploads over mobile links exceeding the hardcoded 120s; user closes the session mid-transcription.
Related errors
- failed to read response: %w
- after %d retries: %w
- decode JSON response: %w
- request failed: %w
- ✗ failed to install skill: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/50c6b3f18f681cb2.
Report an issue: GitHub.