charmbracelet/crush · error
failed to list skills: status code %d
Error message
failed to list skills: status code %d
What it means
Returned by Client.ListSkills when the HTTP request succeeded but the server responded with a status code other than 200. The library expects a 200 with a JSON array of proto.SkillInfo; any non-200 (401 unauthorized, 403 forbidden, 404 unknown workspace, 500 server error) triggers this error. The status code is embedded in the message since the body is not surfaced.
Source
Thrown at internal/client/config.go:227
}
var result struct {
Prompt string `json:"prompt"`
}
if err := json.NewDecoder(rsp.Body).Decode(&result); err != nil {
return "", fmt.Errorf("failed to decode init prompt response: %w", err)
}
return result.Prompt, nil
}
// ListSkills retrieves the visible skills for a workspace.
func (c *Client) ListSkills(ctx context.Context, id string) ([]proto.SkillInfo, error) {
rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/skills", id), nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to list skills: %w", err)
}
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 {View on GitHub (pinned to 7944b8e522)
Solutions
- Parse the status code from the error message to identify the class of failure (4xx vs 5xx).
- For 401/403, refresh credentials or check the workspace access permissions for the current token.
- For 404, verify the workspace ID exists via ListWorkspaces before calling ListSkills.
- For 5xx, check server logs and retry after the server recovers.
- Confirm the client and server versions are compatible (endpoint still exists).
Example fix
// before
if err != nil { log.Fatal(err) } // "failed to list skills: status code 404"
// after
ws, err := client.GetWorkspace(ctx, wsID)
if err != nil || ws == nil {
return fmt.Errorf("workspace %s not found, cannot list skills", wsID)
}
skills, err := client.ListSkills(ctx, wsID) Defensive patterns
Strategy: validation
Validate before calling
// Verify the workspace exists before listing skills
ws, err := client.GetWorkspace(ctx, wsID)
if err != nil || ws == nil {
return fmt.Errorf("workspace %q unavailable", wsID)
} Type guard
func isNon200(err error) (code int, ok bool) {
m := regexp.MustCompile(`status code (\d+)`).FindStringSubmatch(err.Error())
if len(m) < 2 { return 0, false }
code, err2 := strconv.Atoi(m[1])
return code, err2 == nil
} Try / catch
skills, err := client.ListSkills(ctx, wsID)
if err != nil {
if code, ok := isNon200(err); ok {
switch {
case code == 401 || code == 403: reauth()
case code == 404: return ErrWorkspaceNotFound
default: return retryLater(err)
}
}
return err
} Prevention
- Validate workspace IDs against ListWorkspaces before use.
- Refresh auth tokens before long-running flows.
- Keep client and server versions in sync.
- Treat 5xx as retryable with backoff.
When it happens
Trigger: Calling ListSkills with a workspace ID that does not exist (404), an expired or missing auth token (401/403), or when the server has an internal error or is behind a misconfigured reverse proxy (502/503).
Common situations: Stale API credentials after token rotation; typo'd or deleted workspace ID; server version mismatch where the /workspaces/{id}/skills endpoint moved; load balancer returning 502 during deploys.
Related errors
- failed to read skill: status code %d
- failed to enable docker MCP: status code %d
- failed to disable docker MCP: status code %d
- failed to refresh OAuth token: status code %d
- failed to check project init: status code %d
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/175ac83ce3970dd5.
Report an issue: GitHub.