{"record":{"id":"ee73cac337084381","repo":"charmbracelet/crush","slug":"failed-to-create-request-w","errorCode":null,"errorMessage":"failed to create request: %w","messagePattern":"failed to create request: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/agent/tools/download.go","lineNumber":122,"sourceCode":"\t\t\tif !p {\n\t\t\t\treturn NewPermissionDeniedResponse(), nil\n\t\t\t}\n\n\t\t\t// Handle timeout with context\n\t\t\trequestCtx := ctx\n\t\t\tif params.Timeout > 0 {\n\t\t\t\tmaxTimeout := 600 // 10 minutes\n\t\t\t\tif params.Timeout > maxTimeout {\n\t\t\t\t\tparams.Timeout = maxTimeout\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 download from 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\t// Create parent directories if they don't exist\n\t\t\tif err := os.MkdirAll(filepath.Dir(filePath), 0o755); err != nil {\n\t\t\t\treturn fantasy.ToolResponse{}, fmt.Errorf(\"failed to create parent directories: %w\", err)\n\t\t\t}","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/agent/tools/download.go#L104-L140","documentation":"http.NewRequestWithContext failed while constructing the GET request for the download URL. This happens before any network I/O and indicates the URL could not be parsed by net/url (control characters, invalid scheme handling after the earlier prefix check, etc.).","triggerScenarios":"params.URL contains characters invalid in a URL (raw spaces, control characters, unparseable host), producing a *url.Error from url.Parse inside http.NewRequestWithContext.","commonSituations":"LLM passes a URL with unencoded spaces or backslashes; copy-pasted URLs with trailing control characters; malformed IPv6 literals or ports; URLs built by string concatenation.","solutions":["URL-encode the address (url.PathEscape segments or use url.Parse to validate) before calling","Strip whitespace/control characters from the URL","Check the wrapped %w error for the exact parse failure and offending string"],"exampleFix":"// before\nurl := \"https://example.com/my file.zip\"\n// after\nurl := \"https://example.com/my%20file.zip\"","handlingStrategy":"validation","validationCode":"u, err := url.Parse(strings.TrimSpace(rawURL))\nif err != nil || u.Scheme == \"\" || u.Host == \"\" {\n    return nil, fmt.Errorf(\"invalid download URL: %q\", rawURL)\n}","typeGuard":null,"tryCatchPattern":"req, err := http.NewRequestWithContext(ctx, \"GET\", rawURL, nil)\nif err != nil {\n    var urlErr *url.Error\n    if errors.As(err, &urlErr) {\n        return nil, fmt.Errorf(\"bad URL %q: %v\", rawURL, urlErr.Err)\n    }\n    return err\n}","preventionTips":["Validate URLs with url.Parse before passing them to the tool","Percent-encode spaces and special characters in paths","Trim whitespace/control characters from URLs sourced from LLM output or user input"],"tags":["http","url","request"],"backgroundTag":"malformed-url","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}