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

  1. Check reachability from the same machine: curl -v <baseURL>/models with the same Bearer key.
  2. Inspect the wrapped error — a net.DNSError points to DNS, 'connection refused' to the host/port, 'context deadline exceeded' to the 15s timeout.
  3. Set HTTPS_PROXY/HTTP_PROXY if egress goes through a corporate proxy.
  4. 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

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


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/a44d4bdb30cb1f42. Report an issue: GitHub.