{"record":{"id":"1a0e5b1ae01050a7","repo":"grpc/grpc-go","slug":"keepalive-ping-not-acked-within-timeout-s","errorCode":null,"errorMessage":"keepalive ping not acked within timeout %s","messagePattern":"keepalive ping not acked within timeout (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/transport/http2_server.go","lineNumber":1257,"sourceCode":"\t\t\t\t\tt.logger.Infof(\"Closing server transport due to maximum connection age\")\n\t\t\t\t}\n\t\t\t\tt.controlBuf.put(closeConnection{})\n\t\t\tcase <-t.done:\n\t\t\t}\n\t\t\treturn\n\t\tcase <-kpTimer.C:\n\t\t\tlastRead := atomic.LoadInt64(&t.lastRead)\n\t\t\tif lastRead > prevNano {\n\t\t\t\t// There has been read activity since the last time we were\n\t\t\t\t// here. Setup the timer to fire at kp.Time seconds from\n\t\t\t\t// lastRead time and continue.\n\t\t\t\toutstandingPing = false\n\t\t\t\tkpTimer.Reset(time.Duration(lastRead) + t.kp.Time - time.Duration(time.Now().UnixNano()))\n\t\t\t\tprevNano = lastRead\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif outstandingPing && kpTimeoutLeft <= 0 {\n\t\t\t\tt.Close(fmt.Errorf(\"keepalive ping not acked within timeout %s\", t.kp.Timeout))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif !outstandingPing {\n\t\t\t\tif channelz.IsOn() {\n\t\t\t\t\tt.channelz.SocketMetrics.KeepAlivesSent.Add(1)\n\t\t\t\t}\n\t\t\t\tt.controlBuf.put(p)\n\t\t\t\tkpTimeoutLeft = t.kp.Timeout\n\t\t\t\toutstandingPing = true\n\t\t\t}\n\t\t\t// The amount of time to sleep here is the minimum of kp.Time and\n\t\t\t// timeoutLeft. This will ensure that we wait only for kp.Time\n\t\t\t// before sending out the next ping (for cases where the ping is\n\t\t\t// acked).\n\t\t\tsleepDuration := min(t.kp.Time, kpTimeoutLeft)\n\t\t\tkpTimeoutLeft -= sleepDuration\n\t\t\tkpTimer.Reset(sleepDuration)\n\t\tcase <-t.done:","sourceCodeStart":1239,"sourceCodeEnd":1275,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/internal/transport/http2_server.go#L1239-L1275","documentation":"This error occurs in the server's keepalive goroutine when a keepalive ping was sent to the client but no acknowledgment (PING ACK) or any data was received within the configured keepalive timeout period. The server interprets this as a dead connection and closes it.","triggerScenarios":"The server sends a keepalive PING frame (after the keepalive Time interval with no read activity) and the client fails to respond with a PING ACK within keepalive.Timeout seconds. The server calls t.Close with this error, terminating all active streams on the connection.","commonSituations":"Client process crashed/hung without closing the connection (no FIN sent), network partition between client and server, client is alive but its network stack is blocked, very slow client that stops reading/writing, or asymmetric routing where the return path is broken.","solutions":["Verify client health — check if the client process is alive and responsive.","Investigate network connectivity between client and server (firewall, NAT timeout, routing).","If the connection crosses a NAT/firewall, ensure the keepalive interval (Time) is shorter than the NAT idle timeout to prevent silent drops.","Tune keepalive.ServerParameters (Time and Timeout) to match your network's expected latency and reliability characteristics."],"exampleFix":"// before (broken): default keepalive may not suit your network\nsrv := grpc.NewServer()\n\n// after (valid): explicit keepalive tuned for the deployment\nsrv := grpc.NewServer(\n  grpc.KeepaliveParams(keepalive.ServerParameters{\n    Time:    10 * time.Second, // ping every 10s of inactivity\n    Timeout: 5 * time.Second,  // wait 5s for ack before closing\n  }),\n)","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// On the client side, a dead connection results in Unavailable status.\n// gRPC retries automatically for idempotent RPCs. For others:\nif status.Code(err) == codes.Unavailable {\n    // connection was closed by server keepalive timeout\n    // retry the RPC; gRPC will reconnect\n    err = retryRPC(ctx, client, method, req)\n}","preventionTips":["Tune keepalive.ServerParameters Time/Timeout to be shorter than NAT/firewall idle timeouts.","Ensure clients respond promptly to HTTP/2 PING frames (compliant libraries do this automatically).","Monitor server-side connection lifecycle metrics to detect frequent dead connections.","Use grpc.KeepaliveParams on both client and server to maintain liveness across network devices."],"tags":["transport","keepalive","http2","connection","network","timeout"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}