charmbracelet/crush · error
failed to read skill: %w
Error message
failed to read skill: %w
What it means
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.
Source
Thrown at internal/client/config.go:242
}
defer rsp.Body.Close()
if rsp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to list skills: status code %d", rsp.StatusCode)
}
var skills []proto.SkillInfo
if err := json.NewDecoder(rsp.Body).Decode(&skills); err != nil {
return nil, fmt.Errorf("failed to decode skills: %w", err)
}
return skills, nil
}
// ReadSkill reads a skill's content by ID from the server.
func (c *Client) ReadSkill(ctx context.Context, id, skillID string) (*proto.ReadSkillResponse, error) {
rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/skills/read", id), nil, jsonBody(proto.ReadSkillRequest{
SkillID: skillID,
}), http.Header{"Content-Type": []string{"application/json"}})
if err != nil {
return nil, fmt.Errorf("failed to read skill: %w", err)
}
defer rsp.Body.Close()
if rsp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to read skill: status code %d", rsp.StatusCode)
}
var result proto.ReadSkillResponse
if err := json.NewDecoder(rsp.Body).Decode(&result); err != nil {
return nil, fmt.Errorf("failed to decode skill response: %w", err)
}
return &result, nil
}
// MCPResourceContents holds the contents of an MCP resource.
type MCPResourceContents struct {
URI string `json:"uri"`
MIMEType string `json:"mime_type,omitempty"`
Text string `json:"text,omitempty"`
Blob []byte `json:"blob,omitempty"`View on GitHub (pinned to 7944b8e522)
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.
Example fix
// before
c, err := client.ReadSkill(ctx, wsID, skillID)
if err != nil { return err }
// after
c, err := client.ReadSkill(ctx, wsID, skillID)
if err != nil {
if errors.Is(err, context.Canceled) {
return fmt.Errorf("skill read cancelled by caller: %w", err)
}
return fmt.Errorf("could not reach server to read skill: %w", err)
} Defensive patterns
Strategy: try-catch
Validate before calling
// Ensure a deadline exists before the call
if _, ok := ctx.Deadline(); !ok {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, 15*time.Second)
defer cancel()
} Type guard
func isTransportError(err error) bool {
var ne net.Error
return errors.As(err, &ne) || errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded)
} Try / catch
skill, err := client.ReadSkill(ctx, wsID, skillID)
if err != nil {
if isTransportError(err) {
return retryWithBackoff(func() error {
_, err = client.ReadSkill(ctx, wsID, skillID)
return err
})
}
return err
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: Server down or restarted between calls; misconfigured base URL; corporate proxy blocking POST requests; context deadline from a short-lived request scope.
Related errors
- failed to list skills: %w
- failed to enable docker MCP: %w
- failed to disable docker MCP: %w
- failed to make request: %w
- failed to refresh OAuth token: %w
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/e0dfa53126a4e283.
Report an issue: GitHub.