{"record":{"id":"e6e94e2a00cfcba4","repo":"charmbracelet/crush","slug":"status-code-d-s","errorCode":null,"errorMessage":"status code %d: %s","messagePattern":"status code (.+?): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/client/errors.go","lineNumber":53,"sourceCode":"\t// failure as transient.\n\tErrUnsupported = errors.New(\"unsupported by the running server\")\n)\n\n// checkStatus returns nil when rsp's status code is one of ok\n// (http.StatusOK when none are given). Otherwise it returns an error\n// carrying the status code and, when the body decodes as a proto.Error,\n// the server-provided message. Statuses that callers act on are wrapped\n// in the matching sentinel. checkStatus may consume the response body.\nfunc checkStatus(rsp *http.Response, ok ...int) error {\n\tif len(ok) == 0 {\n\t\tok = []int{http.StatusOK}\n\t}\n\tif slices.Contains(ok, rsp.StatusCode) {\n\t\treturn nil\n\t}\n\tvar err error\n\tif msg := decodeErrorMessage(rsp.Body); msg != \"\" {\n\t\terr = fmt.Errorf(\"status code %d: %s\", rsp.StatusCode, msg)\n\t} else {\n\t\terr = fmt.Errorf(\"status code %d\", rsp.StatusCode)\n\t}\n\tswitch rsp.StatusCode {\n\tcase http.StatusNotFound:\n\t\treturn fmt.Errorf(\"%w: %w\", ErrNotFound, err)\n\tcase http.StatusServiceUnavailable:\n\t\treturn fmt.Errorf(\"%w: %w\", ErrServerShuttingDown, err)\n\t}\n\treturn err\n}\n","sourceCodeStart":35,"sourceCodeEnd":65,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/client/errors.go#L35-L65","documentation":"checkStatus validates HTTP responses from the client API. When the status code is not in the accepted list and the response body contains an error message, it produces \"status code %d: %s\" with the server-provided message. This is the client-side representation of a server-side HTTP error.","triggerScenarios":"Any client call routed through checkStatus (RetireClient, CreateWorkspace, GetWorkspace, SetCurrentSession, SubscribeEvents, and anonymous handlers) receiving a non-OK status where decodeErrorMessage extracts a message from the body — e.g. 400/401/403/409/500 with a JSON error payload.","commonSituations":"Sending an invalid workspace payload (400), missing or expired auth (401/403), server-side panic (500), or a client/server version mismatch causing the server to reject the request.","solutions":["Read the message after the colon — it is the server's own error text and usually names the exact problem.","Fix the request payload/parameters indicated by the message (e.g. invalid workspace fields).","Re-authenticate if the status indicates 401/403.","Check client/server version compatibility if the endpoint is reported as unknown or the request shape is rejected."],"exampleFix":"// before: treating any error generically\nws, err := client.CreateWorkspace(ctx, ws)\nif err != nil {\n\treturn err\n}\n// after: inspect status/message for actionable handling\nws, err := client.CreateWorkspace(ctx, ws)\nif err != nil {\n\tvar statusErr *client.StatusError\n\tif errors.As(err, &statusErr) && statusErr.StatusCode == http.StatusUnauthorized {\n\t\treturn refreshAuthAndRetry(ctx)\n\t}\n\treturn fmt.Errorf(\"create workspace rejected by server: %w\", err)\n}","handlingStrategy":"try-catch","validationCode":"// Validate request payload before sending\nif ws == nil || ws.Path == \"\" {\n\treturn errors.New(\"workspace path is required\")\n}","typeGuard":"func isStatusError(err error) (statusCode int, msg string, ok bool) {\n\t// Parse \"status code %d: %s\" produced by checkStatus\n\tconst prefix = \"status code \"\n\ts := err.Error()\n\tif !strings.HasPrefix(s, prefix) {\n\t\treturn 0, \"\", false\n\t}\n\trest := s[len(prefix):]\n\tif idx := strings.IndexByte(rest, ':'); idx >= 0 {\n\t\tcode, e := strconv.Atoi(strings.TrimSpace(rest[:idx]))\n\t\treturn code, strings.TrimSpace(rest[idx+1:]), e == nil\n\t}\n\tcode, e := strconv.Atoi(strings.TrimSpace(rest))\n\treturn code, \"\", e == nil\n}","tryCatchPattern":"result, err := client.CreateWorkspace(ctx, ws)\nif err != nil {\n\tif code, msg, ok := isStatusError(err); ok {\n\t\tswitch code {\n\t\thttp.StatusUnauthorized, http.StatusForbidden:\n\t\t\treturn reauthenticate(ctx)\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"server rejected request (%d): %s\", code, msg)\n\t\t}\n\t}\n\treturn err\n}","preventionTips":["Validate request payloads against the API schema before calling the client.","Keep auth tokens fresh; re-authenticate on 401/403 before retrying.","Match client and daemon versions to avoid rejected request shapes.","Log the full wrapped error message — it contains the server's own explanation."],"tags":["http","api","client","status-code"],"backgroundTag":"http-error-response","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}