charmbracelet/crush · error · ErrServerShuttingDown
server is shutting down
Error message
server is shutting down
What it means
ErrServerShuttingDown (client package) reports that the server refused a request because it has already committed to exiting. checkStatus maps HTTP 503 responses to this sentinel, and admitLocked enforces it server-side. The work is not lost: a replacement server can be started and the request retried against it.
Source
Thrown at internal/client/errors.go:30
var (
// ErrNotFound reports that the server answered 404. For a
// workspace-scoped call this means the server no longer knows the
// workspace — it was torn down, or the server was replaced under the
// client — so the right response is to re-register rather than retry
// the same ID, which can never start succeeding again.
ErrNotFound = errors.New("not found")
// ErrServerBusy reports that the server declined to shut down
// because it is still hosting workspaces or is midway through
// creating one. A client asking a version-mismatched server to stand
// down must keep using it instead of assuming it is going away.
ErrServerBusy = errors.New("server busy")
// ErrServerShuttingDown reports that the server refused the request
// because it has already committed to exiting. The work is not lost:
// a replacement server can be started and the request retried
// against it.
ErrServerShuttingDown = errors.New("server is shutting down")
// ErrUnsupported reports that the running server does not understand
// the request because it predates the feature. Callers must decide
// what is safe to do with an older server rather than treating the
// failure as transient.
ErrUnsupported = errors.New("unsupported by the running server")
)
// checkStatus returns nil when rsp's status code is one of ok
// (http.StatusOK when none are given). Otherwise it returns an error
// carrying the status code and, when the body decodes as a proto.Error,
// the server-provided message. Statuses that callers act on are wrapped
// in the matching sentinel. checkStatus may consume the response body.
func checkStatus(rsp *http.Response, ok ...int) error {
if len(ok) == 0 {
ok = []int{http.StatusOK}
}
if slices.Contains(ok, rsp.StatusCode) {View on GitHub (pinned to 7944b8e522)
Solutions
- Start a replacement server and retry the request against it
- Match with errors.Is(err, client.ErrServerShuttingDown) and never retry the same server
- Raise CRUSH_SERVER_IDLE_TIMEOUT to widen the reuse window and avoid racing shutdown
Example fix
// before
ws, err := client.CreateWorkspace(ctx, path)
if err != nil { return err }
// after
ws, err := client.CreateWorkspace(ctx, path)
if errors.Is(err, client.ErrServerShuttingDown) {
if err := client.StartReplacementServer(ctx); err != nil { return err }
ws, err = client.CreateWorkspace(ctx, path)
}
return err Defensive patterns
Strategy: fallback
Type guard
func isErrServerShuttingDown(err error) bool {
return errors.Is(err, client.ErrServerShuttingDown)
} Try / catch
rsp, err := client.do(req)
if err != nil {
if errors.Is(err, client.ErrServerShuttingDown) {
return retryOnFreshServer(ctx, req) // 503 -> replacement server
}
return err
} Prevention
- Map HTTP 503 to 'start a replacement server', never to transient retry
- Widen CRUSH_SERVER_IDLE_TIMEOUT to avoid attaching during teardown
- Add shutdown barriers in CI so no step talks to a server another step stopped
When it happens
Trigger: Any client call whose response carries http.StatusServiceUnavailable (checkStatus); createWorkspaceOnLiveServer when the live server is tearing down; admitLocked rejecting a request after shutdown began; runSubscription hitting a 503 mid-stream setup.
Common situations: Reusing a server inside its idle shutdown window (DefaultIdleShutdownDelay); a client attaching exactly as the previous server exits; CI races where one step stops the server while another still talks to it.
Related errors
- server is shutting down
- failed to create workspace: server kept shutting down
- server is hosting live workspaces
- not found
- server busy
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/d0950f6c93214a28.
Report an issue: GitHub.