vxcontrol/pentagi · error · RetryableError
failed to send request: %w
Error message
failed to send request: %w
What it means
The POST to api.perplexity.ai failed at the transport level: `client.Do(req)` returned an error before an HTTP response existed. It is wrapped as Retryable (backend/pkg/tools/searchers/perplexity.go:184) because network failures are typically transient — the orchestrator may retry or fall back to another engine.
Source
Thrown at backend/pkg/tools/searchers/perplexity.go:184
reqBody, err := json.Marshal(reqPayload)
if err != nil {
return "", Fatal(fmt.Errorf("failed to marshal request body: %w", err))
}
// Creating HTTP request
req, err := http.NewRequestWithContext(ctx, http.MethodPost, perplexityURL, bytes.NewBuffer(reqBody))
if err != nil {
return "", Fatal(fmt.Errorf("failed to create request: %w", err))
}
// Setting request headers
req.Header.Set("Authorization", "Bearer "+p.apiKey())
req.Header.Set("Content-Type", "application/json")
// Sending the request
resp, err := client.Do(req)
if err != nil {
return "", Retryable(fmt.Errorf("failed to send request: %w", err), 0)
}
defer resp.Body.Close()
// Handling the response. handleErrorResponse keeps the per-status message; the
// retryable/fatal classification is decided here from the status code.
if resp.StatusCode != http.StatusOK {
baseErr := p.handleErrorResponse(resp.StatusCode)
if resp.StatusCode == http.StatusTooManyRequests || resp.StatusCode >= 500 {
return "", Retryable(baseErr, 0)
}
return "", Fatal(baseErr)
}
// Reading the response body
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", Retryable(fmt.Errorf("failed to read response body: %w", err), 0)
}View on GitHub (pinned to ea665308ba)
Solutions
- Read the wrapped cause — it distinguishes DNS, timeout, TLS, and proxy failures
- Test connectivity from the container: `curl -v https://api.perplexity.ai/`
- Fix DNS/proxy/firewall so the container can reach api.perplexity.ai over TLS
- Increase the search timeout (PERPLEXITY timeout config) if deadlines are hit, or rely on the built-in Retryable fallback to another engine
Example fix
// before curl https://api.perplexity.ai # fails: no egress // after: allow egress or set proxy in .env HTTPS_PROXY=http://proxy:3128
Defensive patterns
Strategy: retry
Validate before calling
// egress pre-check before relying on Perplexity
c := &http.Client{Timeout: 5 * time.Second}
if _, err := c.Head("https://api.perplexity.ai"); err != nil {
// mark engine unavailable / rely on fallback engines
} Try / catch
_, err := engine.Handle(ctx, req)
if err != nil {
if searchers.IsRetryable(err) {
// backoff and retry, or fall back to another engine in fallbackStrategy
}
} Prevention
- Ensure container DNS/egress or a valid proxy for api.perplexity.ai
- Set a realistic client timeout for slow networks
- Monitor transport errors in observability (Loki/Grafana) to spot persistent outages vs blips
- Check the wrapped cause to distinguish DNS vs TLS vs timeout before changing infra
When it happens
Trigger: `client.Do` errors on DNS resolution failure, connection refused/timeout, TLS handshake failure, proxy unreachability, or the request context being canceled (deadline/shutdown) during the call.
Common situations: Container without internet access or broken DNS; api.perplexity.ai blocked by firewall/corporate proxy; misconfigured proxy for the HTTP client; client.Timeout too small for slow links; ctx deadline exceeded during heavy flows.
Understand the failure class
Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.
Related errors
- failed to do request: %v
- there are temporarily offline for maintenance. please try ag
- image download stream processing failed: %w
- failed to execute search after %d attempts: %w
- failed to read response body: %w
AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01).
Data as JSON: /api/errors/f238a48bb611b406.
Report an issue: GitHub.