vxcontrol/pentagi · warning · RetryableError
google search failed: %w
Error message
google search failed: %w
What it means
classifyGoogleError wraps any Google Custom Search failure that is NOT a *googleapi.Error into Retryable(fmt.Errorf("google search failed: %w", err)). This is the fallback branch: googleapi.Error means the server answered with an HTTP status (classified via ClassifyHTTPStatus); anything else — DNS failure, TLS error, connection refused, context deadline, JSON decoding — lands here as potentially transient.
Source
Thrown at backend/pkg/tools/searchers/google.go:90
}),
)
obs.LogErrorOrCancel(logger, err, "failed to search in google")
return "", classifyGoogleError(err)
}
return result, nil
}
// classifyGoogleError maps a Google Custom Search failure to a retryable/fatal error.
// A *googleapi.Error carries the upstream HTTP status (429/5xx retryable, 4xx fatal);
// anything else is a transport-level failure, which may clear on retry.
func classifyGoogleError(err error) error {
var gerr *googleapi.Error
if errors.As(err, &gerr) {
return ClassifyHTTPStatus(gerr.Code, "google search failed")
}
return Retryable(fmt.Errorf("google search failed: %w", err), 0)
}
func (g *google) search(ctx context.Context, svc *customsearch.Service, query string, numResults int64) (string, error) {
resp, err := svc.Cse.List().Context(ctx).Cx(g.cxKey()).Q(query).Lr(g.lrKey()).Num(numResults).Do()
if err != nil {
return "", fmt.Errorf("failed to do request: %w", err)
}
return g.formatResults(resp), nil
}
func (g *google) formatResults(res *customsearch.Search) string {
var writer strings.Builder
for i, item := range res.Items {
writer.WriteString(fmt.Sprintf("# %d. %s\n\n", i+1, item.Title))
writer.WriteString(fmt.Sprintf("## URL\n%s\n\n", item.Link))
writer.WriteString(fmt.Sprintf("## Snippet\n\n%s\n\n", item.Snippet))
}View on GitHub (pinned to ea665308ba)
Solutions
- Inspect the wrapped cause to distinguish network vs timeout vs TLS problems
- Verify container egress/proxy settings for www.googleapis.com:443
- Increase the request context timeout if deadline-exceeded is the cause
- Retry: the error is Retryable so the orchestrator's fallback strategy handles transient blips automatically
Defensive patterns
Strategy: retry
Validate before calling
// preflight DNS/egress
conn, err := net.DialTimeout("tcp", "www.googleapis.com:443", 5*time.Second)
if err != nil { // no egress: expect 'google search failed' retries
} Type guard
var gerr *googleapi.Error
func isHTTPLevel(err error) bool { return errors.As(err, &googleapi.Error{} as any) != false && errors.As(err, &gerr) }
// simpler: func isTransportError(err error) bool { return !errors.As(err, new(*googleapi.Error)) } Try / catch
result, err := googleSearcher.Handle(ctx, req)
if err != nil {
if searchers.IsRetryable(err) {
return fallbackSearcher.Handle(ctx, req) // transport blip; another engine serves the query
}
return "", err
} Prevention
- Guarantee container egress to www.googleapis.com:443 or configure a working proxy
- Give requests generous context timeouts
- Rely on the Retryable classification instead of implementing ad-hoc retries
When it happens
Trigger: svc.Cse.List()...Do() returns a non-googleapi error: network unreachable, DNS resolution failure for www.googleapis.com, TLS handshake failure through the proxy, or ctx deadline exceeded mid-request.
Common situations: Backend container without egress to googleapis.com; corporate proxy requiring auth; short context timeouts canceling slow requests; intermittent DNS failures in Docker networks.
Related errors
- could not create Google OpenID client: %w
- temporal window search failed: %w
- entity relationships search failed: %w
- failed to do request: %v
- failed to create google search service: %w
AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01).
Data as JSON: /api/errors/037eda195248feba.
Report an issue: GitHub.