{"record":{"id":"f238a48bb611b406","repo":"vxcontrol/pentagi","slug":"failed-to-send-request-w","errorCode":null,"errorMessage":"failed to send request: %w","messagePattern":"failed to send request: %w","errorType":"exception","errorClass":"RetryableError","httpStatus":null,"severity":"error","filePath":"backend/pkg/tools/searchers/perplexity.go","lineNumber":184,"sourceCode":"\treqBody, err := json.Marshal(reqPayload)\n\tif err != nil {\n\t\treturn \"\", Fatal(fmt.Errorf(\"failed to marshal request body: %w\", err))\n\t}\n\n\t// Creating HTTP request\n\treq, err := http.NewRequestWithContext(ctx, http.MethodPost, perplexityURL, bytes.NewBuffer(reqBody))\n\tif err != nil {\n\t\treturn \"\", Fatal(fmt.Errorf(\"failed to create request: %w\", err))\n\t}\n\n\t// Setting request headers\n\treq.Header.Set(\"Authorization\", \"Bearer \"+p.apiKey())\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\t// Sending the request\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn \"\", Retryable(fmt.Errorf(\"failed to send request: %w\", err), 0)\n\t}\n\tdefer resp.Body.Close()\n\n\t// Handling the response. handleErrorResponse keeps the per-status message; the\n\t// retryable/fatal classification is decided here from the status code.\n\tif resp.StatusCode != http.StatusOK {\n\t\tbaseErr := p.handleErrorResponse(resp.StatusCode)\n\t\tif resp.StatusCode == http.StatusTooManyRequests || resp.StatusCode >= 500 {\n\t\t\treturn \"\", Retryable(baseErr, 0)\n\t\t}\n\t\treturn \"\", Fatal(baseErr)\n\t}\n\n\t// Reading the response body\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn \"\", Retryable(fmt.Errorf(\"failed to read response body: %w\", err), 0)\n\t}","sourceCodeStart":166,"sourceCodeEnd":202,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/tools/searchers/perplexity.go#L166-L202","documentation":"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.","triggerScenarios":"`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.","commonSituations":"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.","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"],"exampleFix":"// before\ncurl https://api.perplexity.ai # fails: no egress\n// after: allow egress or set proxy in .env\nHTTPS_PROXY=http://proxy:3128","handlingStrategy":"retry","validationCode":"// egress pre-check before relying on Perplexity\nc := &http.Client{Timeout: 5 * time.Second}\nif _, err := c.Head(\"https://api.perplexity.ai\"); err != nil {\n    // mark engine unavailable / rely on fallback engines\n}","typeGuard":null,"tryCatchPattern":"_, err := engine.Handle(ctx, req)\nif err != nil {\n    if searchers.IsRetryable(err) {\n        // backoff and retry, or fall back to another engine in fallbackStrategy\n    }\n}","preventionTips":["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"],"tags":["network","http","timeout"],"backgroundTag":"connection-refused","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}