charmbracelet/crush · error

failed to create request: %w

Error message

failed to create request: %w

What it means

searchDuckDuckGo builds an outbound http.NewRequestWithContext GET to the DuckDuckGo lite endpoint; if http.NewRequestWithContext itself errors (it only fails on malformed URLs or an invalid context), this wrapped error is returned before any network I/O. In practice this almost always means the escaped query produced an unparsable URL.

Source

Thrown at internal/agent/tools/search.go:79

	"anomaly-modal",
	"/anomaly.js",
	"Unfortunately, bots use DuckDuckGo too",
}

// ddgLiteEndpoint is a package var so tests can point the search at a
// local httptest server.
var ddgLiteEndpoint = "https://lite.duckduckgo.com/lite/?q="

func searchDuckDuckGo(ctx context.Context, client *http.Client, query string, maxResults int) ([]SearchResult, error) {
	if maxResults <= 0 {
		maxResults = 10
	}

	searchURL := ddgLiteEndpoint + url.QueryEscape(query)

	req, err := http.NewRequestWithContext(ctx, "GET", searchURL, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	setRandomizedHeaders(req)

	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("failed to execute search: %w", err)
	}
	defer resp.Body.Close()

	// A 202 from DuckDuckGo is the anomaly-challenge interstitial, not a
	// result page; report throttling rather than parsing it into an
	// empty result set.
	if resp.StatusCode == http.StatusAccepted {
		return nil, errSearchRateLimited
	}
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("search failed with status code: %d", resp.StatusCode)

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Sanitize the query: strip control characters/newlines before calling the tool
  2. Inspect the wrapped error's URL parse message for the offending character
  3. Verify the search endpoint configuration hasn't been overridden with an invalid URL
  4. Check ctx cancellation if constructing with a pre-canceled context

Example fix

// before
query = strings.TrimSpace(rawQuery) // may still contain \n or control bytes
// after
query = strings.Map(func(r rune) rune {
    if r < 0x20 { return -1 }
    return r
}, rawQuery)
Defensive patterns

Strategy: validation

Validate before calling

func sanitizeQuery(q string) string {
    return strings.Map(func(r rune) rune {
        if r < 0x20 || r == 0x7f { return -1 }
        return r
    }, strings.TrimSpace(q))
}
query = sanitizeQuery(query)

Try / catch

var urlErr *url.Error
if err := searchDuckDuckGo(ctx, q); err != nil && errors.As(err, &urlErr) {
    // urlErr.Op == "parse": malformed URL — fix the query/endpoint
}

Prevention

When it happens

Trigger: http.NewRequestWithContext(ctx, "GET", ddgLiteEndpoint+url.QueryEscape(query), nil) returns an error — typically a control character or malformed URL after escaping, or an already-canceled ctx passed at construction.

Common situations: Query strings containing raw newlines/control bytes forwarded from model output; endpoint constant corrupted by config override; passing a canceled context.

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/d37e4a689e43dc44. Report an issue: GitHub.