{"record":{"id":"50c6b3f18f681cb2","repo":"sipeed/picoclaw","slug":"failed-to-send-request-w","errorCode":null,"errorMessage":"failed to send request: %w","messagePattern":"failed to send request: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/audio/asr/elevenlabs_transcriber.go","lineNumber":110,"sourceCode":"\treq, err := http.NewRequestWithContext(ctx, \"POST\", url, &requestBody)\n\tif err != nil {\n\t\tlogger.ErrorCF(\"voice\", \"Failed to create request\", map[string]any{\"error\": err})\n\t\treturn nil, fmt.Errorf(\"failed to create request: %w\", err)\n\t}\n\n\treq.Header.Set(\"Content-Type\", writer.FormDataContentType())\n\treq.Header.Set(\"Xi-Api-Key\", t.apiKey)\n\n\tlogger.DebugCF(\"voice\", \"Sending transcription request to ElevenLabs API\", map[string]any{\n\t\t\"url\":                url,\n\t\t\"request_size_bytes\": requestBody.Len(),\n\t\t\"file_size_bytes\":    fileInfo.Size(),\n\t})\n\n\tresp, err := t.httpClient.Do(req)\n\tif err != nil {\n\t\tlogger.ErrorCF(\"voice\", \"Failed to send request\", map[string]any{\"error\": err})\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\tlogger.ErrorCF(\"voice\", \"Failed to read response\", map[string]any{\"error\": err})\n\t\treturn nil, fmt.Errorf(\"failed to read response: %w\", err)\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tlogger.ErrorCF(\"voice\", \"ElevenLabs API error\", map[string]any{\n\t\t\t\"status_code\": resp.StatusCode,\n\t\t\t\"response\":    string(body),\n\t\t})\n\t\treturn nil, fmt.Errorf(\"ElevenLabs API error (status %d): %s\", resp.StatusCode, string(body))\n\t}\n\n\tlogger.DebugCF(\"voice\", \"Received response from ElevenLabs API\", map[string]any{","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/audio/asr/elevenlabs_transcriber.go#L92-L128","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nresp, err := transcriber.Transcribe(ctx, path)\nif err != nil { return err }\n\n// after\nresp, err := transcriber.Transcribe(ctx, path)\nif err != nil {\n    var netErr net.Error\n    if errors.As(err, &netErr) && netErr.Timeout() {\n        return retryWithBackoff(ctx, func() (*asr.TranscriptionResponse, error) {\n            return transcriber.Transcribe(ctx, path)\n        })\n    }\n    if errors.Is(err, context.Canceled) { return err }\n    return err\n}","handlingStrategy":"retry","validationCode":"// Optional preflight (cheap, catches DNS/proxy/egress issues early):\nif err := preflightEndpoint(ctx, \"https://api.elevenlabs.io\", 2*time.Second); err != nil {\n    return fmt.Errorf(\"cannot reach ElevenLabs: %w\", err)\n}","typeGuard":"func isTransientTransport(err error) bool {\n    var netErr net.Error\n    if errors.As(err, &netErr) && netErr.Timeout() { return true }\n    if errors.Is(err, syscall.ECONNRESET) || errors.Is(err, syscall.ECONNREFUSED) { return true }\n    if errors.Is(err, context.DeadlineExceeded) { return true }\n    return false\n}","tryCatchPattern":"var lastErr error\nfor attempt := 0; attempt < 3; attempt++ {\n    resp, err := el.Transcribe(ctx, path)\n    if err == nil { return resp, nil }\n    lastErr = err\n    if errors.Is(err, context.Canceled) || !isTransientTransport(err) { break }\n    time.Sleep(time.Duration(1<<attempt) * time.Second)\n}\nreturn nil, fmt.Errorf(\"ElevenLabs transcription failed: %w\", lastErr)","preventionTips":["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."],"tags":["network","timeout","proxy","elevenlabs","retry","go"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}