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

  1. Align client keepalive.Time to be >= server keepalive enforcement MinTime (server default is 5 minutes if configured, or 2 hours without streams).
  2. Disable client keepalive.PermitWithoutStream if the server doesn't permit pings without active streams, or ensure pings only happen when streams exist.
  3. Configure the server's keepalive enforcement policy (grpc.KeepaliveEnforcementPolicy) with a MinTime that accommodates your clients, and set PermitWithoutStream=true if needed.
  4. 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

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


AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11). Data as JSON: /api/errors/c6af59276a8923e0. Report an issue: GitHub.