{"record":{"id":"e0dfa53126a4e283","repo":"charmbracelet/crush","slug":"failed-to-read-skill-w","errorCode":null,"errorMessage":"failed to read skill: %w","messagePattern":"failed to read skill: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/client/config.go","lineNumber":242,"sourceCode":"\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 {\n\t\treturn nil, fmt.Errorf(\"failed to read skill: %w\", err)\n\t}\n\tdefer rsp.Body.Close()\n\tif rsp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"failed to read skill: status code %d\", rsp.StatusCode)\n\t}\n\tvar result proto.ReadSkillResponse\n\tif err := json.NewDecoder(rsp.Body).Decode(&result); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to decode skill response: %w\", err)\n\t}\n\treturn &result, nil\n}\n\n// MCPResourceContents holds the contents of an MCP resource.\ntype MCPResourceContents struct {\n\tURI      string `json:\"uri\"`\n\tMIMEType string `json:\"mime_type,omitempty\"`\n\tText     string `json:\"text,omitempty\"`\n\tBlob     []byte `json:\"blob,omitempty\"`","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/client/config.go#L224-L260","documentation":"This error is returned by Client.ReadSkill when the HTTP POST request to /workspaces/{id}/skills/read fails at the transport level, before any HTTP status could be evaluated. Like the other client errors, the underlying cause is wrapped with %w so callers can inspect it. It means the round trip to read the skill content never completed.","triggerScenarios":"Calling ReadSkill(ctx, workspaceID, skillID) when the connection is refused/reset, DNS resolution fails, the request times out, or the context is cancelled before the response arrives.","commonSituations":"Server down or restarted between calls; misconfigured base URL; corporate proxy blocking POST requests; context deadline from a short-lived request scope.","solutions":["Confirm the server is reachable and the base URL is correct.","Inspect the wrapped cause with errors.Is/As to distinguish timeout vs connection refused.","Extend the context timeout if reading large skills triggers timeouts.","Retry the POST once with backoff for transient network faults.","Check proxy/firewall rules allow POST to this endpoint."],"exampleFix":"// before\nc, err := client.ReadSkill(ctx, wsID, skillID)\nif err != nil { return err }\n// after\nc, err := client.ReadSkill(ctx, wsID, skillID)\nif err != nil {\n    if errors.Is(err, context.Canceled) {\n        return fmt.Errorf(\"skill read cancelled by caller: %w\", err)\n    }\n    return fmt.Errorf(\"could not reach server to read skill: %w\", err)\n}","handlingStrategy":"try-catch","validationCode":"// Ensure a deadline exists before the call\nif _, ok := ctx.Deadline(); !ok {\n    var cancel context.CancelFunc\n    ctx, cancel = context.WithTimeout(ctx, 15*time.Second)\n    defer cancel()\n}","typeGuard":"func isTransportError(err error) bool {\n    var ne net.Error\n    return errors.As(err, &ne) || errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded)\n}","tryCatchPattern":"skill, err := client.ReadSkill(ctx, wsID, skillID)\nif err != nil {\n    if isTransportError(err) {\n        return retryWithBackoff(func() error {\n            _, err = client.ReadSkill(ctx, wsID, skillID)\n            return err\n        })\n    }\n    return err\n}","preventionTips":["Always bound ReadSkill with a context timeout.","Don't tie the call to a short-lived request context that may be cancelled.","Retry POSTs idempotently with backoff.","Monitor server uptime; alert on transport failures."],"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"}