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

  1. Parse the status code from the error message to identify the class of failure (4xx vs 5xx).
  2. For 401/403, refresh credentials or check the workspace access permissions for the current token.
  3. For 404, verify the workspace ID exists via ListWorkspaces before calling ListSkills.
  4. For 5xx, check server logs and retry after the server recovers.
  5. 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

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


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/175ac83ce3970dd5. Report an issue: GitHub.