sipeed/picoclaw · error
request failed: %w
Error message
request failed: %w
What it means
The HTTP round trip itself failed: client.Do returned an error before any response existed. DNS resolution, TCP connect, TLS handshake, proxy errors, and the hard-coded 15-second client timeout all surface here, with the original error wrapped.
Source
Thrown at cmd/picoclaw/internal/model/online.go:43
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)
if err != nil {
return nil, fmt.Errorf("read response: %w", err)
}
// {"data": [...]} envelope. Distinguish "envelope shape with empty list"
// from "object without a data key" via Data being non-nil after unmarshal:View on GitHub (pinned to 49183d7e8d)
Solutions
- Check reachability from the same machine: curl -v <baseURL>/models with the same Bearer key.
- Inspect the wrapped error — a net.DNSError points to DNS, 'connection refused' to the host/port, 'context deadline exceeded' to the 15s timeout.
- Set HTTPS_PROXY/HTTP_PROXY if egress goes through a corporate proxy.
- If the provider is genuinely slow, note the timeout is fixed at 15s in code — use a faster endpoint or raise the constant locally.
Example fix
// before: api_base = "https://api.openai.co/v1" (typo -> DNS failure) // after: api_base = "https://api.openai.com/v1"
Defensive patterns
Strategy: retry
Validate before calling
host := u.Hostname()
if _, err := net.LookupHost(host); err != nil {
return fmt.Errorf("cannot resolve %s: %w", host, err)
} Try / catch
resp, err := client.Do(req)
if err != nil {
var nerr net.Error
if errors.As(err, &nerr) && nerr.Timeout() {
// timeout: retry once with backoff before giving up
}
return fmt.Errorf("request failed: %w", err)
} Prevention
- Wrap model listing in a bounded retry (2-3 attempts, backoff) for transient DNS/TCP failures.
- Keep the api base pinned to a known-good host in config templates.
- Export HTTPS_PROXY in CI environments where egress goes through a proxy.
When it happens
Trigger: Calling fetchOpenAIModels when the api base host does not resolve, the connection is refused, the TLS cert is untrusted, a proxy blocks egress, or the /models call exceeds the 15s http.Client timeout (online.go:43).
Common situations: Machine offline or behind a VPN, wrong hostname in the api base, corporate proxy requiring HTTPS_PROXY, self-signed certificate on a local OpenAI-compatible server, or a slow provider stalling past 15 seconds.
Related errors
- failed to send request: %w
- failed to get WeCom QR code: %w
- unexpected status %s
- read error response: %w
- read response: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/a44d4bdb30cb1f42.
Report an issue: GitHub.