{"record":{"id":"64d60e551dbf2e84","repo":"charmbracelet/crush","slug":"server-health-check-failed-s","errorCode":null,"errorMessage":"server health check failed: %s","messagePattern":"server health check failed: (.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/client/client.go","lineNumber":100,"sourceCode":"\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer rsp.Body.Close()\n\tif err := json.NewDecoder(rsp.Body).Decode(&cfg); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &cfg, nil\n}\n\n// Health checks the server's health status.\nfunc (c *Client) Health(ctx context.Context) error {\n\trsp, err := c.get(ctx, \"/health\", nil, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer rsp.Body.Close()\n\tif rsp.StatusCode != http.StatusOK {\n\t\treturn fmt.Errorf(\"server health check failed: %s\", rsp.Status)\n\t}\n\treturn nil\n}\n\n// VersionInfo retrieves the server's version information.\nfunc (c *Client) VersionInfo(ctx context.Context) (*proto.VersionInfo, error) {\n\tvar vi proto.VersionInfo\n\trsp, err := c.get(ctx, \"version\", nil, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer rsp.Body.Close()\n\tif err := json.NewDecoder(rsp.Body).Decode(&vi); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &vi, nil\n}\n","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/client/client.go#L82-L118","documentation":"Client.Health performs a GET /health against the server; this error is returned when the HTTP response status is anything other than 200 OK. The message carries rsp.Status (e.g. \"503 Service Unavailable\"), so it reflects the server's own reported unhealthiness rather than a transport failure (transport errors are returned directly by c.get).","triggerScenarios":"Calling Health(ctx) and receiving a non-200 status: server is starting up or shutting down, an intermediate proxy returns 502/503/504, wrong port hitting a different service, or authentication gateway rejecting the request.","commonSituations":"Client started before server finished listening behind a proxy; stale port from a previous run; reverse proxy with health checks disabled; server process crashed but socket still answered via supervisor.","solutions":["Read rsp.Status in the message to identify the exact HTTP code returned.","Confirm the server process is running and listening on the configured address.","Retry with backoff if the server is still starting (503 during boot is common).","Verify no proxy intercepts the request; query the server address directly."],"exampleFix":"// before\nif err := client.Health(ctx); err != nil { return err }\n// after\n// tolerate transient unavailability during startup\nvar lastErr error\nfor i := 0; i < 5; i++ {\n    if err := client.Health(ctx); err == nil {\n        lastErr = nil\n        break\n    } else {\n        lastErr = err\n        time.Sleep(time.Duration(1<<i) * 200 * time.Millisecond)\n    }\n}\nif lastErr != nil { return lastErr }","handlingStrategy":"retry","validationCode":"conn, err := net.DialTimeout(\"tcp\", serverAddr, 2*time.Second)\nif err != nil {\n    return fmt.Errorf(\"server not reachable at %s: %w\", serverAddr, err)\n}\nconn.Close()","typeGuard":"func IsUnhealthyStatus(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"server health check failed\")\n}","tryCatchPattern":"if err := client.Health(ctx); err != nil {\n    if IsUnhealthyStatus(err) {\n        // retry with exponential backoff; 503 during startup is transient\n        return retryHealth(ctx, client, 5)\n    }\n    return err\n}","preventionTips":["Wait for the server's ready signal before first health probe.","Bypass proxies when probing the control endpoint directly.","Persist the actual listening port, don't assume a default.","Use bounded exponential backoff for startup-time health checks."],"tags":["http","health-check","network","client"],"backgroundTag":"http-health-check-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}