{"record":{"id":"4a30d54deab3407d","repo":"charmbracelet/crush","slug":"failed-to-fetch-url-w","errorCode":null,"errorMessage":"failed to fetch URL: %w","messagePattern":"failed to fetch URL: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/agent/tools/fetch.go","lineNumber":122,"sourceCode":"\t\t\tif params.Timeout > 0 {\n\t\t\t\tif params.Timeout > maxFetchTimeoutSeconds {\n\t\t\t\t\tparams.Timeout = maxFetchTimeoutSeconds\n\t\t\t\t}\n\t\t\t\tvar cancel context.CancelFunc\n\t\t\t\trequestCtx, cancel = context.WithTimeout(ctx, time.Duration(params.Timeout)*time.Second)\n\t\t\t\tdefer cancel()\n\t\t\t}\n\n\t\t\treq, err := http.NewRequestWithContext(requestCtx, \"GET\", params.URL, nil)\n\t\t\tif err != nil {\n\t\t\t\treturn fantasy.ToolResponse{}, fmt.Errorf(\"failed to create request: %w\", err)\n\t\t\t}\n\n\t\t\treq.Header.Set(\"User-Agent\", \"crush/1.0\")\n\n\t\t\tresp, err := client.Do(req)\n\t\t\tif err != nil {\n\t\t\t\treturn fantasy.ToolResponse{}, fmt.Errorf(\"failed to fetch URL: %w\", err)\n\t\t\t}\n\t\t\tdefer resp.Body.Close()\n\n\t\t\tif resp.StatusCode != http.StatusOK {\n\t\t\t\treturn fantasy.NewTextErrorResponse(fmt.Sprintf(\"Request failed with status code: %d\", resp.StatusCode)), nil\n\t\t\t}\n\n\t\t\tbody, err := io.ReadAll(io.LimitReader(resp.Body, MaxFetchSize))\n\t\t\tif err != nil {\n\t\t\t\treturn fantasy.NewTextErrorResponse(\"Failed to read response body: \" + err.Error()), nil\n\t\t\t}\n\n\t\t\tcontent := string(body)\n\n\t\t\tvalidUTF8 := utf8.ValidString(content)\n\t\t\tif !validUTF8 {\n\t\t\t\treturn fantasy.NewTextErrorResponse(\"Response content is not valid UTF-8\"), nil\n\t\t\t}","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/agent/tools/fetch.go#L104-L140","documentation":"The fetch tool wraps any error from the HTTP client's Do() call (network-level failures) with 'failed to fetch URL'. This happens before any HTTP response is received, so it covers DNS resolution failures, connection refusals/timeouts, TLS errors, and malformed URLs that escape request creation. The wrapped underlying error is preserved via %w.","triggerScenarios":"client.Do(req) returns a non-nil error: DNS lookup failure for the hostname, connection refused or timed out, TLS handshake failure, or an unsupported/invalid URL scheme that passed http.NewRequestWithContext.","commonSituations":"Fetching a URL behind a firewall or proxy, offline environment, typo in hostname (e.g. missing scheme causing odd behavior or bad host), self-signed certificates, or an HTTP proxy misconfigured via HTTP_PROXY/HTTPS_PROXY env vars.","solutions":["Verify the URL is reachable with `curl -v <url>` from the same machine","Check the wrapped cause (%w) to distinguish DNS vs connection vs TLS issues","If behind a proxy, check HTTP_PROXY/HTTPS_PROXY/NO_PROXY environment variables","For self-signed TLS, fix the certificate chain rather than disabling verification"],"exampleFix":"// before\nresp, err := client.Do(req)\nif err != nil {\n    return fantasy.ToolResponse{}, fmt.Errorf(\"failed to fetch URL: %w\", err)\n}\n// after\nresp, err := client.Do(req)\nif err != nil {\n    var urlErr *url.Error\n    if errors.As(err, &urlErr) {\n        return fantasy.ToolResponse{}, fmt.Errorf(\"failed to fetch URL %s: %w\", url, err)\n    }\n    return fantasy.ToolResponse{}, fmt.Errorf(\"failed to fetch URL: %w\", err)\n}","handlingStrategy":"try-catch","validationCode":"u, err := url.Parse(rawURL)\nif err != nil || (u.Scheme != \"http\" && u.Scheme != \"https\") || u.Host == \"\" {\n    return fmt.Errorf(\"invalid URL: %q\", rawURL)\n}","typeGuard":"var dnsErr *net.DNSError\nif errors.As(err, &dnsErr) {\n    // host could not be resolved\n}","tryCatchPattern":"resp, err := client.Do(req)\nif err != nil {\n    return fmt.Errorf(\"failed to fetch URL: %w\", err) // inspect wrapped cause with errors.As/Is\n}","preventionTips":["Validate URL scheme and host before fetching","Test reachability with curl when a fetch fails","Check proxy env vars in sandboxed or corporate environments","Apply a sane http.Client timeout so failures are fast and clear"],"tags":["network","http","dns","timeout"],"backgroundTag":"http-request-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}