{"record":{"id":"175ac83ce3970dd5","repo":"charmbracelet/crush","slug":"failed-to-list-skills-status-code-d","errorCode":null,"errorMessage":"failed to list skills: status code %d","messagePattern":"failed to list skills: status code (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/client/config.go","lineNumber":227,"sourceCode":"\t}\n\tvar result struct {\n\t\tPrompt string `json:\"prompt\"`\n\t}\n\tif err := json.NewDecoder(rsp.Body).Decode(&result); err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to decode init prompt response: %w\", err)\n\t}\n\treturn result.Prompt, nil\n}\n\n// ListSkills retrieves the visible skills for a workspace.\nfunc (c *Client) ListSkills(ctx context.Context, id string) ([]proto.SkillInfo, error) {\n\trsp, err := c.get(ctx, fmt.Sprintf(\"/workspaces/%s/skills\", id), nil, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to list skills: %w\", err)\n\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 {","sourceCodeStart":209,"sourceCodeEnd":245,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/client/config.go#L209-L245","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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)."],"exampleFix":"// before\nif err != nil { log.Fatal(err) } // \"failed to list skills: status code 404\"\n// after\nws, err := client.GetWorkspace(ctx, wsID)\nif err != nil || ws == nil {\n    return fmt.Errorf(\"workspace %s not found, cannot list skills\", wsID)\n}\nskills, err := client.ListSkills(ctx, wsID)","handlingStrategy":"validation","validationCode":"// Verify the workspace exists before listing skills\nws, err := client.GetWorkspace(ctx, wsID)\nif err != nil || ws == nil {\n    return fmt.Errorf(\"workspace %q unavailable\", wsID)\n}","typeGuard":"func isNon200(err error) (code int, ok bool) {\n    m := regexp.MustCompile(`status code (\\d+)`).FindStringSubmatch(err.Error())\n    if len(m) < 2 { return 0, false }\n    code, err2 := strconv.Atoi(m[1])\n    return code, err2 == nil\n}","tryCatchPattern":"skills, err := client.ListSkills(ctx, wsID)\nif err != nil {\n    if code, ok := isNon200(err); ok {\n        switch {\n        case code == 401 || code == 403: reauth()\n        case code == 404: return ErrWorkspaceNotFound\n        default: return retryLater(err)\n        }\n    }\n    return err\n}","preventionTips":["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."],"tags":["http-client","http-status","skills","api-error"],"backgroundTag":"http-non-200-response","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}