vxcontrol/pentagi · error · Retryable

failed to do request: %v

Error message

failed to do request: %v

What it means

Thrown in tavily.search when the HTTP POST to https://api.tavily.com/search fails at the transport level (client.Do returned an error), before any HTTP status is available. The searcher wraps the underlying net/http error as Retryable(err, 0) so the web_search orchestrator may retry with fallback engines. It means DNS failure, TCP connect failure, TLS failure, request cancellation (context), or a proxy problem — not a Tavily API rejection.

Source

Thrown at backend/pkg/tools/searchers/tavily.go:134

		IncludeRawContent: true,
		MaxResults:        maxResults,
	}
	reqBody, err := json.Marshal(reqPayload)
	if err != nil {
		return "", Fatal(fmt.Errorf("failed to marshal request body: %v", err))
	}

	req, err := http.NewRequest(http.MethodPost, tavilyURL, bytes.NewBuffer(reqBody))
	if err != nil {
		return "", Fatal(fmt.Errorf("failed to build request: %v", err))
	}

	req = req.WithContext(ctx)
	req.Header.Set("Content-Type", "application/json")

	resp, err := client.Do(req)
	if err != nil {
		return "", Retryable(fmt.Errorf("failed to do request: %v", err), 0)
	}
	defer resp.Body.Close()

	return t.parseHTTPResponse(ctx, resp)
}

func (t *tavily) parseHTTPResponse(ctx context.Context, resp *http.Response) (string, error) {
	switch resp.StatusCode {
	case http.StatusOK:
		var respBody tavilySearchResult
		if err := json.NewDecoder(resp.Body).Decode(&respBody); err != nil {
			return "", Fatal(fmt.Errorf("failed to decode response body: %v", err))
		}
		return t.buildTavilyResult(ctx, &respBody), nil
	case http.StatusBadRequest:
		return "", Fatal(fmt.Errorf("request is invalid"))
	case http.StatusUnauthorized:
		return "", Fatal(fmt.Errorf("API key is wrong"))

View on GitHub (pinned to ea665308ba)

Solutions

  1. Check basic connectivity from the same container: curl -v https://api.tavily.com/search — fix DNS/firewall/proxy if it fails.
  2. If behind a proxy, verify HTTPS_PROXY/HTTP_PROXY env vars and that the proxy CA cert is trusted by the container.
  3. If the context deadline is the cause, increase the caller's timeout for the web_search tool.
  4. Check status.tavily.com or retry later if Tavily itself is down; ensure a fallback engine (e.g. DuckDuckGo) is configured in the web_search fallback chain.
  5. Since the error is Retryable, verify the orchestrator retry policy is enabled so transient blips self-heal.

Example fix

// before: cryptic transport failure inside the sandbox
client, err := system.GetHTTPClient(t.cfg)
// after: fail fast with a clear config check and honor ctx deadline
if t.cfg.TavilyAPIKey == "" { return "", ErrNotConfigured }
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
resp, err := client.Do(req)
Defensive patterns

Strategy: retry

Validate before calling

// check egress before calling the engine
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.tavily.com", nil)
if _, err := http.DefaultClient.Do(req); err != nil {
    return fmt.Errorf("no egress to api.tavily.com: %w", err)
}

Type guard

func isRetryableTransportErr(err error) bool {
    return errors.Is(err, context.DeadlineExceeded) ||
        errors.Is(err, context.Canceled) ||
        errors.Is(err, syscall.ECONNREFUSED) ||
        errors.Is(err, syscall.EHOSTUNREACH) ||
        errors.Is(err, io.EOF)
}

Try / catch

result, err := searcher.Handle(ctx, req)
if err != nil {
    var rerr *RetryableError
    if errors.As(err, &rerr) {
        // back off and retry, then fall back to another engine
    }
    return err
}

Prevention

When it happens

Trigger: client.Do(req) in search (tavily.go:132) returns err: network unreachable, DNS resolution failure of api.tavily.com, TLS handshake failure, proxy misconfiguration (system.GetHTTPClient with proxy settings), or ctx cancelled/timed out mid-request.

Common situations: No internet access or DNS failure in the Docker sandbox; corporate proxy or self-signed MITM cert (HTTPS_PROXY / custom CA) misconfigured; Tavily outage or regional block; request deadline exceeded because the search took too long; IPv6-only environment that cannot reach the API.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01). Data as JSON: /api/errors/8de6d2d5b30b5a75. Report an issue: GitHub.