{"record":{"id":"13d9f51360b7e45c","repo":"charmbracelet/crush","slug":"unsupported-by-the-running-server","errorCode":null,"errorMessage":"unsupported by the running server","messagePattern":"unsupported by the running server","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/client/errors.go","lineNumber":36,"sourceCode":"\tErrNotFound = errors.New(\"not found\")\n\n\t// ErrServerBusy reports that the server declined to shut down\n\t// because it is still hosting workspaces or is midway through\n\t// creating one. A client asking a version-mismatched server to stand\n\t// down must keep using it instead of assuming it is going away.\n\tErrServerBusy = errors.New(\"server busy\")\n\n\t// ErrServerShuttingDown reports that the server refused the request\n\t// because it has already committed to exiting. The work is not lost:\n\t// a replacement server can be started and the request retried\n\t// against it.\n\tErrServerShuttingDown = errors.New(\"server is shutting down\")\n\n\t// ErrUnsupported reports that the running server does not understand\n\t// the request because it predates the feature. Callers must decide\n\t// what is safe to do with an older server rather than treating the\n\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 {","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/client/errors.go#L18-L54","documentation":"ErrUnsupported in internal/client/errors.go is a sentinel returned when the running crush server predates an RPC/feature the client is trying to use. Unlike transient failures, it signals a protocol/feature-version mismatch: the server understood the connection but not this specific request. Callers (e.g. ShutdownServerIfIdle, RetireClient, restartIfStale) wrap it so older-server situations can be distinguished from network or availability errors.","triggerScenarios":"Calling client.ShutdownServerIfIdle or client.RetireClient against a server binary older than the feature; restartIfStale attempting the shutdown RPC on a stale server that lacks it; any request that checkStatus maps to the unsupported sentinel.","commonSituations":"Mixed-version setups: a newly installed crush client talking to a long-running server daemon started by an older version; skipping an upgrade so background server processes keep the old build alive; CI environments with cached old server binaries.","solutions":["Restart or upgrade the running crush server so client and server versions match.","Check the error with errors.Is(err, client.ErrUnsupported) and take a safe fallback for older servers (e.g. skip graceful shutdown, just kill the process).","Ensure no stale server process is left over (pkill old crush daemon) before starting the new client."],"exampleFix":"// before\nif err := client.ShutdownServerIfIdle(ctx); err != nil {\n    return err\n}\n// after\nif err := client.ShutdownServerIfIdle(ctx); err != nil {\n    if errors.Is(err, client.ErrUnsupported) {\n        // Old server: safe to proceed without graceful shutdown.\n        return nil\n    }\n    return err\n}","handlingStrategy":"type-guard","validationCode":"// Compare client and server versions before calling feature-specific RPCs\nif clientVersion != serverVersion {\n    log.Println(\"version mismatch possible: some RPCs may be unsupported\")\n}","typeGuard":"func isUnsupported(err error) bool { return errors.Is(err, client.ErrUnsupported) }","tryCatchPattern":"if err := c.ShutdownServerIfIdle(ctx); err != nil {\n    if isUnsupported(err) {\n        // older server: take safe fallback\n        return nil\n    }\n    return err\n}","preventionTips":["Keep client and server binaries on the same version; restart stale daemons after upgrading.","Always use errors.Is against ErrUnsupported instead of string matching.","Design callers with explicit older-server fallbacks rather than treating the error as transient."],"tags":["go","versioning","rpc","client-server"],"backgroundTag":"server-feature-not-supported","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}