{"record":{"id":"75b9407c6f2742af","repo":"grpc/grpc-go","slug":"connection-active-but-health-check-failed-status","errorCode":null,"errorMessage":"connection active but health check failed. status=%s","messagePattern":"connection active but health check failed\\. status=(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"health/client.go","lineNumber":113,"sourceCode":"\n\t\t\t// Reports healthy for the LBing purposes if health check is not implemented in the server.\n\t\t\tif status.Code(err) == codes.Unimplemented {\n\t\t\t\tsetConnectivityState(connectivity.Ready, nil)\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\t// Reports unhealthy if server's Watch method gives an error other than UNIMPLEMENTED.\n\t\t\tif err != nil {\n\t\t\t\tsetConnectivityState(connectivity.TransientFailure, fmt.Errorf(\"connection active but received health check RPC error: %v\", err))\n\t\t\t\tcontinue retryConnection\n\t\t\t}\n\n\t\t\t// As a message has been received, removes the need for backoff for the next retry by resetting the try count.\n\t\t\ttryCnt = 0\n\t\t\tif resp.Status == healthpb.HealthCheckResponse_SERVING {\n\t\t\t\tsetConnectivityState(connectivity.Ready, nil)\n\t\t\t} else {\n\t\t\t\tsetConnectivityState(connectivity.TransientFailure, fmt.Errorf(\"connection active but health check failed. status=%s\", resp.Status))\n\t\t\t}\n\t\t}\n\t}\n}\n","sourceCodeStart":95,"sourceCodeEnd":118,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/health/client.go#L95-L118","documentation":"Produced by clientHealthCheck when the server's Health/Watch RPC returns a HealthCheckResponse with a status other than SERVING (i.e. NOT_SERVING, SERVICE_UNKNOWN, or UNKNOWN). The connection is set to TRANSIENT_FAILURE even though the transport is healthy, because the application layer reports the service is not ready.","triggerScenarios":"Server explicitly reports NOT_SERVING during shutdown or dependency failure; requesting a health check for a service name the server does not know (SERVICE_UNKNOWN); server-side health status set to non-serving by the application's SetServingStatus call.","commonSituations":"Rolling deploy where the new pod reports NOT_SERVING until ready; dependency (DB, downstream service) outage causing the server to mark itself unhealthy; querying health for an unregistered service name; graceful shutdown signaling.","solutions":["Confirm the server is intentionally non-serving (check its health.SetServingStatus call sites).","Verify the service name in the health check request matches a name the server registered.","Wait for the server to transition back to SERVING; the client auto-recovers.","During deploys, ensure readiness probes align with SetServingStatus(true)."],"exampleFix":"// server side: ensure you flip serving status correctly\n// before\nhealthServer.SetServingStatus(\"my.Service\", healthpb.HealthCheckResponse_NOT_SERVING)\n// after, once dependencies are ready\nhealthServer.SetServingStatus(\"my.Service\", healthpb.HealthCheckResponse_SERVING)","handlingStrategy":"fallback","validationCode":null,"typeGuard":"// Inspect a HealthCheckResponse to decide whether to keep traffic away.\nfunc isServing(resp *healthpb.HealthCheckResponse) bool {\n    return resp.GetStatus() == healthpb.HealthCheckResponse_SERVING\n}","tryCatchPattern":"// On the client, route around a non-serving backend via the LB policy.\nresp, err := healthClient.Check(ctx, &healthpb.HealthCheckRequest{Service: svc})\nif err != nil || !isServing(resp) {\n    // pick another backend / shed load\n}","preventionTips":["Make server SetServingStatus reflect real dependency health.","Use distinct service names so SERVICE_UNKNOWN does not mask a real check.","Coordinate readiness probes with SetServingStatus(true) during startup."],"tags":["grpc","health-checking","connectivity","serving-status","load-balancing"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}