sipeed/picoclaw · error
build request: %w
Error message
build request: %w
What it means
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).
Source
Thrown at cmd/picoclaw/internal/model/online.go:37
Data []modelEntry `json:"data"`
}
// fetchOpenAIModels GETs <baseURL>/models with Bearer auth and accepts both the
// {data:[…]} envelope and a bare array shape used by various OpenAI-compatible servers.
func fetchOpenAIModels(baseURL, apiKey string) ([]modelEntry, error) {
if strings.TrimSpace(baseURL) == "" {
return nil, fmt.Errorf("api base is required")
}
if strings.TrimSpace(apiKey) == "" {
return nil, fmt.Errorf("api key is required")
}
url := strings.TrimRight(baseURL, "/") + "/models"
client := &http.Client{Timeout: 15 * time.Second}
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("build request: %w", err)
}
req.Header.Set("Authorization", "Bearer "+apiKey)
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, readErr := io.ReadAll(io.LimitReader(resp.Body, 512))
if readErr != nil {
return nil, fmt.Errorf("read error response: %w", readErr)
}
return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, strings.TrimSpace(string(body)))
}
body, err := io.ReadAll(resp.Body)View on GitHub (pinned to 49183d7e8d)
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.
Example fix
// before apiBase := "api.example.com/v1" // no scheme -> NewRequest fails // after apiBase := "https://api.example.com/v1"
Defensive patterns
Strategy: validation
Validate before calling
u, err := url.Parse(strings.TrimRight(baseURL, "/") + "/models")
if err != nil || u.Scheme == "" || u.Host == "" {
return fmt.Errorf("invalid api base %q", baseURL)
}
req, err := http.NewRequest(http.MethodGet, u.String(), nil) Type guard
func validBaseURL(s string) bool {
u, err := url.Parse(s)
return err == nil && u.Scheme != "" && u.Host != ""
} Try / catch
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
var uerr *url.Error
if errors.As(err, &uerr) {
// inspect uerr for the exact parse failure before surfacing
}
return fmt.Errorf("build request: %w", err)
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- create request: %w
- invalid WeCom QR generate URL: %w
- invalid WeCom QR query URL: %w
- invalid WeCom QR page URL: %w
- MCP server ${server.name} requires a URL.
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/f9dcb4c12f0fabaa.
Report an issue: GitHub.