{"record":{"id":"16bf29a54038c89b","repo":"charmbracelet/crush","slug":"failed-to-list-skills-w","errorCode":null,"errorMessage":"failed to list skills: %w","messagePattern":"failed to list skills: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/client/config.go","lineNumber":223,"sourceCode":"\t}\n\tdefer rsp.Body.Close()\n\tif rsp.StatusCode != http.StatusOK {\n\t\treturn \"\", fmt.Errorf(\"failed to get init prompt: status code %d\", rsp.StatusCode)\n\t}\n\tvar result struct {\n\t\tPrompt string `json:\"prompt\"`\n\t}\n\tif err := json.NewDecoder(rsp.Body).Decode(&result); err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to decode init prompt response: %w\", err)\n\t}\n\treturn result.Prompt, nil\n}\n\n// ListSkills retrieves the visible skills for a workspace.\nfunc (c *Client) ListSkills(ctx context.Context, id string) ([]proto.SkillInfo, error) {\n\trsp, err := c.get(ctx, fmt.Sprintf(\"/workspaces/%s/skills\", id), nil, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to list skills: %w\", err)\n\t}\n\tdefer rsp.Body.Close()\n\tif rsp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"failed to list skills: status code %d\", rsp.StatusCode)\n\t}\n\tvar skills []proto.SkillInfo\n\tif err := json.NewDecoder(rsp.Body).Decode(&skills); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to decode skills: %w\", err)\n\t}\n\treturn skills, nil\n}\n\n// ReadSkill reads a skill's content by ID from the server.\nfunc (c *Client) ReadSkill(ctx context.Context, id, skillID string) (*proto.ReadSkillResponse, error) {\n\trsp, err := c.post(ctx, fmt.Sprintf(\"/workspaces/%s/skills/read\", id), nil, jsonBody(proto.ReadSkillRequest{\n\t\tSkillID: skillID,\n\t}), http.Header{\"Content-Type\": []string{\"application/json\"}})\n\tif err != nil {","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/client/config.go#L205-L241","documentation":"This error is returned by Client.ListSkills when the underlying HTTP GET request to /workspaces/{id}/skills fails at the transport level. The library wraps the original error (connection failure, timeout, context cancellation, invalid URL) with %w so the root cause remains inspectable via errors.Is/As. It indicates the request never completed successfully enough to receive an HTTP response.","triggerScenarios":"Calling Client.ListSkills(ctx, workspaceID) when the server is unreachable, the connection is refused/reset, the context deadline expires mid-request, or c.get fails to construct/execute the request.","commonSituations":"Server process not running or wrong base URL/port configured; network partition, VPN or proxy blocking the request; context cancelled because a parent operation timed out; TLS certificate issues in self-hosted setups.","solutions":["Verify the client's base URL/host points at a running server (curl the /health or any endpoint).","Check network connectivity to the server (DNS, firewall, proxy, VPN).","Inspect the wrapped cause with errors.Unwrap or %v to see the transport error (e.g. connection refused vs timeout).","Increase or fix the context deadline passed to ListSkills if timeouts are the cause.","Retry with backoff if the failure is transient (server restarting)."],"exampleFix":"// before\nskills, err := client.ListSkills(ctx, wsID)\nif err != nil { return err }\n// after\nskills, err := client.ListSkills(ctx, wsID)\nif err != nil {\n    if errors.Is(err, context.DeadlineExceeded) {\n        return fmt.Errorf(\"skills listing timed out, check server health: %w\", err)\n    }\n    return fmt.Errorf(\"is the server running? %w\", err)\n}","handlingStrategy":"try-catch","validationCode":"// Go: pre-check server reachability\nfunc serverReachable(baseURL string) error {\n    resp, err := http.Get(baseURL + \"/health\")\n    if err != nil { return err }\n    defer resp.Body.Close()\n    return nil\n}","typeGuard":"func isTransportError(err error) bool {\n    var ne net.Error\n    return errors.As(err, &ne) || errors.Is(err, context.DeadlineExceeded) || errors.Is(err, syscall.ECONNREFUSED)\n}","tryCatchPattern":"skills, err := client.ListSkills(ctx, wsID)\nif err != nil {\n    if isTransportError(err) {\n        // retry or surface 'server unreachable'\n        return retryOrFallback(err)\n    }\n    return err\n}","preventionTips":["Health-check the server before batch operations.","Always pass a context with a sensible timeout.","Log errors.Unwrap output to distinguish timeout vs refusal.","Pin and validate the base URL per environment."],"tags":["http-client","network","skills","wrapped-error"],"backgroundTag":"http-request-transport-failure","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}