{"record":{"id":"f9dcb4c12f0fabaa","repo":"sipeed/picoclaw","slug":"build-request-w","errorCode":null,"errorMessage":"build request: %w","messagePattern":"build request: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/picoclaw/internal/model/online.go","lineNumber":37,"sourceCode":"\tData []modelEntry `json:\"data\"`\n}\n\n// fetchOpenAIModels GETs <baseURL>/models with Bearer auth and accepts both the\n// {data:[…]} envelope and a bare array shape used by various OpenAI-compatible servers.\nfunc fetchOpenAIModels(baseURL, apiKey string) ([]modelEntry, error) {\n\tif strings.TrimSpace(baseURL) == \"\" {\n\t\treturn nil, fmt.Errorf(\"api base is required\")\n\t}\n\tif strings.TrimSpace(apiKey) == \"\" {\n\t\treturn nil, fmt.Errorf(\"api key is required\")\n\t}\n\n\turl := strings.TrimRight(baseURL, \"/\") + \"/models\"\n\n\tclient := &http.Client{Timeout: 15 * time.Second}\n\treq, err := http.NewRequest(http.MethodGet, url, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"build request: %w\", err)\n\t}\n\treq.Header.Set(\"Authorization\", \"Bearer \"+apiKey)\n\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"request failed: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tbody, readErr := io.ReadAll(io.LimitReader(resp.Body, 512))\n\t\tif readErr != nil {\n\t\t\treturn nil, fmt.Errorf(\"read error response: %w\", readErr)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"HTTP %d: %s\", resp.StatusCode, strings.TrimSpace(string(body)))\n\t}\n\n\tbody, err := io.ReadAll(resp.Body)","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/cmd/picoclaw/internal/model/online.go#L19-L55","documentation":"http.NewRequest returned an error while constructing the GET to strings.TrimRight(baseURL,\"/\") + \"/models\"; the underlying error is wrapped with %w. Because the method and body are fixed, NewRequest practically only fails when the assembled URL cannot be parsed (invalid characters, malformed host or port, missing scheme).","triggerScenarios":"A baseURL that passes the empty-check but cannot be parsed — e.g. \"api.example.com/v1\" (no scheme), \"http://host:bad-port\", or a value containing spaces/control characters — reaches online.go:37 and url.Parse rejects it inside http.NewRequest.","commonSituations":"Typo in the api base config value (missing https://), stray quotes or trailing punctuation pasted into config.json, or the base URL assembled from an unset/wrong environment variable.","solutions":["Read the wrapped cause — Go's url.Error names the exact parse failure (e.g. 'invalid port after host').","Fix the api base to a full URL with scheme and host, e.g. https://api.example.com/v1.","Validate the configured base URL with url.Parse at config load time so bad values fail early.","Check for stray spaces, quotes, or smart quotes copied from docs/chat."],"exampleFix":"// before\napiBase := \"api.example.com/v1\" // no scheme -> NewRequest fails\n// after\napiBase := \"https://api.example.com/v1\"","handlingStrategy":"validation","validationCode":"u, err := url.Parse(strings.TrimRight(baseURL, \"/\") + \"/models\")\nif err != nil || u.Scheme == \"\" || u.Host == \"\" {\n    return fmt.Errorf(\"invalid api base %q\", baseURL)\n}\nreq, err := http.NewRequest(http.MethodGet, u.String(), nil)","typeGuard":"func validBaseURL(s string) bool {\n    u, err := url.Parse(s)\n    return err == nil && u.Scheme != \"\" && u.Host != \"\"\n}","tryCatchPattern":"req, err := http.NewRequest(http.MethodGet, url, nil)\nif err != nil {\n    var uerr *url.Error\n    if errors.As(err, &uerr) {\n        // inspect uerr for the exact parse failure before surfacing\n    }\n    return fmt.Errorf(\"build request: %w\", err)\n}","preventionTips":["Validate provider base URLs once at config load, not per request.","Build URLs with url.URL and JoinPath instead of string concatenation.","Unit-test URL construction over your config fixtures."],"tags":["url","http","config","validation","go"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}