{"record":{"id":"a1de8993c5f97355","repo":"charmbracelet/crush","slug":"server-health-check-failed-s-a1de89","errorCode":null,"errorMessage":"server health check failed: %s","messagePattern":"server health check failed: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cmd/root.go","lineNumber":747,"sourceCode":"\n// probeHealth issues a single GET to the readiness endpoint and treats\n// any 2xx response as success.\nfunc probeHealth(ctx context.Context, h *http.Client, reqURL string, hostURL *url.URL) error {\n\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, reqURL, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif hostURL.Scheme == \"unix\" || hostURL.Scheme == \"npipe\" {\n\t\treq.Host = client.DummyHost\n\t}\n\trsp, err := h.Do(req)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer rsp.Body.Close()\n\t_, _ = io.Copy(io.Discard, rsp.Body)\n\tif rsp.StatusCode < 200 || rsp.StatusCode >= 300 {\n\t\treturn fmt.Errorf(\"server health check failed: %s\", rsp.Status)\n\t}\n\treturn nil\n}\n\n// restartIfStale checks whether the running server matches the current\n// client version. When they differ it asks the server to stand down and,\n// if it agrees, removes the stale socket so the caller can start a fresh\n// server.\n//\n// The request is conditional and the server has the last word: it refuses\n// while it is hosting anything, because BuildID derives from the\n// executable's mtime, so any rebuild (including every `go run`) makes a\n// second session look like an upgrade and would otherwise kill the first\n// session's workspaces. Servers too old to understand the conditional\n// command are left running for the same reason — the request they do\n// understand is unconditional. They shut themselves down when they go\n// idle, and the next client then finds no socket and spawns a current one.\n//","sourceCodeStart":729,"sourceCodeEnd":765,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/cmd/root.go#L729-L765","documentation":"probeHealth performs an HTTP GET against the server's health endpoint and requires a 2xx status. Any response outside 200–299 is returned as 'server health check failed: <status>'. This surfaces servers that are listening but reporting an unhealthy or unexpected HTTP state.","triggerScenarios":"The health endpoint responds with 404/405 (wrong route or method), 500/503 (server-side init failure or shutting down), or a proxy in front returns an error status; the response body is discarded and only rsp.Status is reported.","commonSituations":"A version-mismatched server exposing a different health route; the server is mid-shutdown and returning 503; a misconfigured HTTP_PROXY intercepting localhost requests; the server's dependencies (DB, config) failed to load, causing 500s.","solutions":["Read the status code in the message: 404/405 suggests route/version mismatch — make client and server versions match.","Check server logs for the underlying 5xx cause (failed config load, DB error).","Unset HTTP(S)_PROXY for localhost or add NO_PROXY=localhost so probes hit the server directly.","Restart the server cleanly (kill process, remove socket and server dir) and let crush respawn it."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"req, _ := http.NewRequestWithContext(ctx, http.MethodGet, healthURL, nil)\nrsp, err := http.DefaultClient.Do(req)\nif err == nil && rsp.StatusCode >= 200 && rsp.StatusCode < 300 {\n\t// server healthy before calling ensureServer-dependent APIs\n}","typeGuard":null,"tryCatchPattern":"err := probeHealth(ctx, httpClient, reqURL, hostURL)\nif err != nil {\n\tvar he *healthStatusError\n\tif errors.As(err, &he) && he.StatusCode >= 500 {\n\t\t// transient server state: back off and retry\n\t\ttime.Sleep(time.Second)\n\t\terr = probeHealth(ctx, httpClient, reqURL, hostURL)\n\t}\n}","preventionTips":["Set NO_PROXY=localhost/127.0.0.1 so probes bypass proxies","Keep server and client versions aligned so the health route matches","Check server logs for 5xx causes (config load, DB init failures)","Retry on 5xx; treat 4xx as a version/route mismatch requiring a restart"],"tags":["http","health-check","server","status-code"],"backgroundTag":"health-check-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}