{"record":{"id":"a44d4bdb30cb1f42","repo":"sipeed/picoclaw","slug":"request-failed-w","errorCode":null,"errorMessage":"request failed: %w","messagePattern":"request failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/picoclaw/internal/model/online.go","lineNumber":43,"sourceCode":"\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)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read response: %w\", err)\n\t}\n\n\t// {\"data\": [...]} envelope. Distinguish \"envelope shape with empty list\"\n\t// from \"object without a data key\" via Data being non-nil after unmarshal:","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/cmd/picoclaw/internal/model/online.go#L25-L61","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before: api_base = \"https://api.openai.co/v1\" (typo -> DNS failure)\n// after:  api_base = \"https://api.openai.com/v1\"","handlingStrategy":"retry","validationCode":"host := u.Hostname()\nif _, err := net.LookupHost(host); err != nil {\n    return fmt.Errorf(\"cannot resolve %s: %w\", host, err)\n}","typeGuard":null,"tryCatchPattern":"resp, err := client.Do(req)\nif err != nil {\n    var nerr net.Error\n    if errors.As(err, &nerr) && nerr.Timeout() {\n        // timeout: retry once with backoff before giving up\n    }\n    return fmt.Errorf(\"request failed: %w\", err)\n}","preventionTips":["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."],"tags":["network","http","timeout","dns","go"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}