grpc/grpc-go · error
ErrCodeEnhanceYourCalm
ErrCodeEnhanceYourCalm
Error message
too_many_pings
What it means
Sent as an HTTP/2 GOAWAY frame with code ENHANCE_YOUR_CALM when the server's keepalive enforcement detects the client is sending PING frames too frequently (more than maxPingStrikes=2 strikes). The server tracks ping strikes when pings arrive faster than the configured keepalive policy interval (MinTime, or defaultPingTimeout when no streams are active and PermitWithoutStream is false). After exceeding 2 strikes, the server sends this GOAWAY and closes the connection.
Source
Thrown at internal/transport/http2_server.go:924
t.mu.Lock()
ns := len(t.activeStreams)
t.mu.Unlock()
if ns < 1 && !t.kep.PermitWithoutStream {
// Keepalive shouldn't be active thus, this new ping should
// have come after at least defaultPingTimeout.
if t.lastPingAt.Add(defaultPingTimeout).After(now) {
t.pingStrikes++
}
} else {
// Check if keepalive policy is respected.
if t.lastPingAt.Add(t.kep.MinTime).After(now) {
t.pingStrikes++
}
}
if t.pingStrikes > maxPingStrikes {
// Send goaway and close the connection.
t.controlBuf.put(&goAway{code: http2.ErrCodeEnhanceYourCalm, debugData: []byte("too_many_pings"), closeConn: errors.New("got too many pings from the client")})
}
}
func (t *http2Server) handleWindowUpdate(f *http2.WindowUpdateFrame) {
t.controlBuf.put(&incomingWindowUpdate{
streamID: f.Header().StreamID,
increment: f.Increment,
})
}
func appendHeaderFieldsFromMD(headerFields []hpack.HeaderField, md metadata.MD) []hpack.HeaderField {
for k, vv := range md {
if isReservedHeader(k) {
// Clients don't tolerate reading restricted headers after some non restricted ones were sent.
continue
}
for _, v := range vv {
headerFields = append(headerFields, hpack.HeaderField{Name: k, Value: encodeMetadataHeader(k, v)})View on GitHub (pinned to 0c51461d27)
Solutions
- Align client keepalive.Time to be >= server keepalive enforcement MinTime (server default is 5 minutes if configured, or 2 hours without streams).
- Disable client keepalive.PermitWithoutStream if the server doesn't permit pings without active streams, or ensure pings only happen when streams exist.
- Configure the server's keepalive enforcement policy (grpc.KeepaliveEnforcementPolicy) with a MinTime that accommodates your clients, and set PermitWithoutStream=true if needed.
- Check for buggy HTTP/2 clients or proxies that send excessive pings.
Example fix
// Client side: align keepalive with server policy
// before (too aggressive)
conn, _ := grpc.Dial(addr,
grpc.WithKeepaliveParams(keepalive.ClientParameters{
Time: 1 * time.Second, // too frequent!
PermitWithoutStream: true,
}),
)
// after (respect server's enforcement policy)
conn, _ := grpc.Dial(addr,
grpc.WithKeepaliveParams(keepalive.ClientParameters{
Time: 30 * time.Second, // >= server MinTime
Timeout: 10 * time.Second,
PermitWithoutStream: false,
}),
) Defensive patterns
Strategy: validation
Validate before calling
// Align client keepalive with server enforcement policy.
// Validate before dialing:
kp := keepalive.ClientParameters{
Time: 30 * time.Second, // must be >= server MinTime
Timeout: 10 * time.Second,
PermitWithoutStream: false,
}
conn, err := grpc.Dial(addr, grpc.WithKeepaliveParams(kp)) Try / catch
// Retry on connection close due to too_many_pings with adjusted keepalive.
err := client.Call(ctx, req)
if err != nil && strings.Contains(err.Error(), "too_many_pings") {
log.Print("server rejected pings; increasing keepalive interval")
// reconnect with less aggressive keepalive
conn.Close()
conn = reconnectWithKeepalive(60 * time.Second)
} Prevention
- Set client keepalive.Time to be >= server's KeepaliveEnforcementPolicy.MinTime.
- Avoid setting very low keepalive intervals (e.g., < 10s) unless the server permits it.
- Coordinate client and server keepalive policies across your fleet.
- Monitor for GOAWAY/EnhanceYourCalm events as a signal of policy mismatch.
When it happens
Trigger: A client sends HTTP/2 PING frames more frequently than the server's keepalive policy allows. Each out-of-policy ping increments pingStrikes; when it exceeds maxPingStrikes (2), the server at http2_server.go:922-924 sends GOAWAY(EnhanceYourCalm, 'too_many_pings') and closes the connection. This commonly occurs with aggressive client-side keepalive settings.
Common situations: Client configured with keepalive.Time too low (e.g., 1s) hitting a server with a higher MinTime (default ~5min for gRPC servers, or 2h defaultPingTimeout without streams); client keepalive with PermitWithoutStream enabled sending pings during idle; a misbehaving or non-gRPC HTTP/2 client; multiple clients behind a load balancer multiplexing onto one connection with aggressive pings.
Related errors
- gRPC requires HTTP/2
- keepalive ping not acked within timeout %s
- failed to write the HTTP request: %v
- credentials: rawConn is dispatched out of gRPC
- invalid gRPC request method %q
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/c6af59276a8923e0.
Report an issue: GitHub.