charmbracelet/crush · error
failed to decode skill response: %w
Error message
failed to decode skill response: %w
What it means
Returned by Client.ReadSkill when the server returned 200 OK but the body failed to decode into proto.ReadSkillResponse. Typical causes are an empty body, malformed JSON, or a payload whose fields don't match the proto.ReadSkillResponse struct. It signals a broken response contract rather than a request problem.
Source
Thrown at internal/client/config.go:250
}
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"`
}
// EnableDockerMCP enables the Docker MCP server on the workspace.
func (c *Client) EnableDockerMCP(ctx context.Context, id string) error {
rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/mcp/docker/enable", id), nil, nil, nil)
if err != nil {
return fmt.Errorf("failed to enable docker MCP: %w", err)
}View on GitHub (pinned to 7944b8e522)
Solutions
- Capture the raw body via server logs or a proxy to see the actual 200 payload.
- Compare the payload against proto.ReadSkillResponse's json tags and update the mismatched side.
- Upgrade client or server so both agree on the read-skills response schema.
- Check for body-corrupting middleware (compression, charset, truncation).
- Reproduce with curl -X POST .../skills/read and validate the JSON with jq.
Example fix
// before
r, err := client.ReadSkill(ctx, wsID, skillID) // decode error on 200
// after
// Verify shape: curl -s -X POST $BASE/workspaces/$WS/skills/read -d '{"skill_id":"..."}' | jq
r, err := client.ReadSkill(ctx, wsID, skillID)
if err != nil {
return fmt.Errorf("unexpected read-skill response; check server version: %w", err)
} Defensive patterns
Strategy: type-guard
Type guard
func isDecodeError(err error) bool {
return err != nil && strings.Contains(err.Error(), "failed to decode skill response")
}
func validReadSkillResponse(r *proto.ReadSkillResponse) bool {
return r != nil && r.Content != ""
} Try / catch
skill, err := client.ReadSkill(ctx, wsID, skillID)
if err != nil {
if isDecodeError(err) {
// contract mismatch on 200 body; fall back or surface version skew
return fmt.Errorf("server response incompatible: %w", err)
}
return err
}
if !validReadSkillResponse(skill) {
return ErrMalformedSkillPayload
} Prevention
- Validate the response shape with curl/jq after server upgrades.
- Version-gate client/server compatibility.
- Guard against middleware that mutates response bodies.
- After decode, validate required fields before use.
When it happens
Trigger: Server handler for /workspaces/{id}/skills/read returns 200 with an error JSON object, null body, truncated output, or renamed fields inconsistent with proto.ReadSkillResponse.
Common situations: Server version drift after an API change; middleware or compression misconfiguration corrupting the body; a skill whose content contains bytes that break encoding on the server side.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- failed to decode skills: %w
- failed to list skills: %w
- failed to list skills: status code %d
- failed to read skill: %w
- failed to read skill: status code %d
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/16b711ed224feb79.
Report an issue: GitHub.