vxcontrol/pentagi · error

failed to create search request: %w

Error message

failed to create search request: %w

What it means

http.NewRequestWithContext fails while constructing the POST request to the DuckDuckGo HTML endpoint. In net/http this errors only on an unparseable URL or invalid method/ctx-nil combination, so this wrap is a defensive guard around the constant duckduckgoSearchURL. It aborts the search immediately (no retry) and becomes a Fatal engine failure in Handle.

Source

Thrown at backend/pkg/tools/searchers/duckduckgo.go:150

// search performs a web search using DuckDuckGo
func (d *duckduckgo) search(ctx context.Context, query string, maxResults int) (string, error) {
	// Build form data for POST request
	formData := d.buildFormData(query)

	// Create HTTP client with proper configuration
	client, err := system.GetHTTPClient(d.cfg)
	if err != nil {
		return "", fmt.Errorf("failed to create http client: %w", err)
	}

	client.Timeout = duckduckgoTimeout

	// Execute request with retry logic
	var response *searchResponse
	for attempt := 0; attempt < duckduckgoMaxRetries; attempt++ {
		req, err := http.NewRequestWithContext(ctx, "POST", duckduckgoSearchURL, strings.NewReader(formData))
		if err != nil {
			return "", fmt.Errorf("failed to create search request: %w", err)
		}

		// Add necessary headers for POST request
		req.Header.Set("User-Agent", duckduckgoUserAgent)
		req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
		req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
		req.Header.Set("Accept-Language", "en-US,en;q=0.9")

		resp, err := client.Do(req)
		if err != nil {
			if attempt == duckduckgoMaxRetries-1 {
				return "", fmt.Errorf("failed to execute search after %d attempts: %w", duckduckgoMaxRetries, err)
			}
			select {
			case <-ctx.Done():
				return "", ctx.Err()
			case <-time.After(time.Second):
			}

View on GitHub (pinned to ea665308ba)

Solutions

  1. Read the wrapped error: it names the parse problem with the URL; fix the duckduckgoSearchURL constant or its source value.
  2. Assert the URL parses at startup (url.Parse in an init/test) to fail fast after edits.
  3. If the URL became configurable, validate it before passing to NewRequestWithContext.

Example fix

// before
duckduckgoSearchURL = "https://duckduckgo.com/ html"
// after
duckduckgoSearchURL = "https://duckduckgo.com/html/"
Defensive patterns

Strategy: validation

Validate before calling

if _, err := url.Parse(duckduckgoSearchURL); err != nil {
    panic(fmt.Sprintf("duckduckgoSearchURL invalid: %v", err))
}

Try / catch

req, err := http.NewRequestWithContext(ctx, "POST", duckduckgoSearchURL, strings.NewReader(formData))
if err != nil {
    return "", fmt.Errorf("failed to create search request: %w", err)
}

Prevention

When it happens

Trigger: Only when the request URL is malformed or the method string is invalid. Since duckduckgoSearchURL and "POST" are compile-time constants, this is practically unreachable unless the URL constant is edited to an invalid value or the code is changed to interpolate a bad URL.

Common situations: A developer edits duckduckgoSearchURL and introduces whitespace/typo; refactoring the URL to come from config with a bad value; nil context misuse after refactor.

Related errors


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