charmbracelet/crush · error

failed to fetch URL: %w

Error message

failed to fetch URL: %w

What it means

Wraps a client.Do transport error when POSTing the GraphQL query to sourcegraph.com (internal/agent/tools/sourcegraph.go:123-126). No HTTP response was received at all: DNS failure, connection refused/reset, TLS errors, or the request context (tool Timeout param, max 120s, or client's 30s default) expired. Distinct from the non-200 branch, which produces a text error response instead.

Source

Thrown at internal/agent/tools/sourcegraph.go:125

			}
			graphqlQuery := string(graphqlQueryBytes)

			req, err := http.NewRequestWithContext(
				requestCtx,
				"POST",
				"https://sourcegraph.com/.api/graphql",
				bytes.NewBuffer([]byte(graphqlQuery)),
			)
			if err != nil {
				return fantasy.ToolResponse{}, fmt.Errorf("failed to create request: %w", err)
			}

			req.Header.Set("Content-Type", "application/json")
			req.Header.Set("User-Agent", "crush/1.0")

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

			if resp.StatusCode != http.StatusOK {
				body, _ := io.ReadAll(resp.Body)
				if len(body) > 0 {
					return fantasy.NewTextErrorResponse(fmt.Sprintf("Request failed with status code: %d, response: %s", resp.StatusCode, string(body))), nil
				}

				return fantasy.NewTextErrorResponse(fmt.Sprintf("Request failed with status code: %d", resp.StatusCode)), nil
			}
			body, err := io.ReadAll(resp.Body)
			if err != nil {
				return fantasy.ToolResponse{}, fmt.Errorf("failed to read response body: %w", err)
			}

			var result map[string]any
			if err = json.Unmarshal(body, &result); err != nil {

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Check basic connectivity to sourcegraph.com (curl https://sourcegraph.com/.api/graphql) and DNS.
  2. Verify no proxy env vars (HTTP_PROXY/HTTPS_PROXY) or firewall rules block the host; configure a working proxy if required.
  3. Retry with a larger Timeout param or after transient network failures; errors.Is(err, context.DeadlineExceeded) indicates a timeout.
  4. Wait for Sourcegraph service recovery if status.sourcegraph.com reports an outage.

Example fix

// before
tool := NewSourcegraphTool(nil) // 30s default client

// after
client := &http.Client{Timeout: 90 * time.Second}
tool := NewSourcegraphTool(client)
Defensive patterns

Strategy: retry

Validate before calling

resp, err := http.Head("https://sourcegraph.com")
if err != nil {
    return fmt.Errorf("sourcegraph.com unreachable: %w", err)
}
resp.Body.Close()

Type guard

var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() { /* timeout, not unreachable host */ }
if errors.Is(err, context.DeadlineExceeded) { /* tool Timeout param too small */ }

Try / catch

out, err := toolCall(ctx, params)
if err != nil {
    var netErr net.Error
    if errors.As(err, &netErr) && netErr.Timeout() {
        // retry with larger Timeout param
    } else if isNetworkUnreachable(err) {
        // report connectivity problem to user
    }
    return err
}

Prevention

When it happens

Trigger: client.Do(req) returns an error: no internet/DNS resolution failure for sourcegraph.com, connection refused or reset, TLS handshake failure, context deadline exceeded from params.Timeout or the 30s default http.Client.Timeout, or a proxy blocking the request.

Common situations: Offline or air-gapped environments running the agent; corporate firewalls/proxies blocking sourcegraph.com; Sourcegraph outages; a large search with a small Timeout value exceeding the client's 30-second limit.

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 charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/7e2ade9f5d58cb0d. Report an issue: GitHub.