grpc/grpc-go · error

received -bytes data exceeding the limit bytes

Error message

received %d-bytes data exceeding the limit %d bytes

What it means

Fires in inFlow.onData (flowcontrol.go:182) when received-but-unconsumed data plus pending window updates exceed the flow-control window (limit + delta). This is the receiver-side enforcement of HTTP/2 flow control: the peer sent more DATA bytes than the advertised window allowed, which is a protocol violation by the peer, so the connection/stream must error out.

Solutions

  1. Capture the error's reported limit and received bytes; if received is only slightly over, suspect a race or a peer accounting bug.
  2. Update the peer's HTTP/2/gRPC stack to a conformant version, or replace a misbehaving intermediary (proxy/LB).
  3. If you control the receiver, ensure the application drains streams promptly so window updates are sent (slow readers can compound flow-control stress, though this specific error indicates a real overrun, not just backpressure).
  4. File/inspect channelz metrics for the connection to confirm which peer stream triggered the overrun.

Example fix

// This is a peer-side protocol violation; no local code change 'fixes' it.
// Mitigate by handling the RPC error and upgrading/replacing the peer.

stream, _ := client.SomeMethod(ctx)
for {
    if _, err := stream.Recv(); err != nil {
        if status.Code(err) == codes.ResourceExhausted || isFlowControl(err) {
            log.Printf("peer overran flow control; reconnecting: %v", err)
            // back off, reconnect, or report upstream
        }
        break
    }
}
Defensive patterns

Strategy: try-catch

Try / catch

if _, err := stream.Recv(); err != nil {
    // flow-control overrun surfaces as a transport/RPC error;
    // check status and reconnect or report the peer.
    st, _ := status.FromError(err)
    log.Printf("stream error (possible flow-control overrun): code=%s msg=%s", st.Code(), st.Message())
}

Prevention

When it happens

Trigger: A remote peer (client or server) sends DATA frames whose cumulative bytes exceed the window gRPC advertised. Caused by a buggy/non-conformant HTTP/2 stack on the peer, a corrupted window-update sequence, or a peer that ignores WINDOW_UPDATE/RST_STREAM accounting. The exact numbers are reported: received bytes vs the window limit.

Common situations: Interop with a broken HTTP/2 implementation; a proxy or load balancer that mishandles flow control; streaming very large messages where a race or bug causes the peer to overrun; memory pressure where the receiver shrinks the window but the peer doesn't honor it.

Related errors


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

Appendix: source

Thrown at internal/transport/flowcontrol.go:182

			// estUntransmittedData and estSenderQuota. This will be helpful in case the message
			// is padded; We will fallback on the current available window(at least a 1/4th of the limit).
			f.delta = n
		}
		return f.delta
	}
	return 0
}

// onData is invoked when some data frame is received. It updates pendingData.
func (f *inFlow) onData(n uint32) error {
	f.mu.Lock()
	defer f.mu.Unlock()

	f.pendingData += n
	if f.pendingData+f.pendingUpdate > f.limit+f.delta {
		limit := f.limit
		rcvd := f.pendingData + f.pendingUpdate
		return fmt.Errorf("received %d-bytes data exceeding the limit %d bytes", rcvd, limit)
	}
	return nil
}

// onRead is invoked when the application reads the data. It returns the window size
// to be sent to the peer.
func (f *inFlow) onRead(n uint32) uint32 {
	f.mu.Lock()
	defer f.mu.Unlock()

	if f.pendingData == 0 {
		return 0
	}
	f.pendingData -= n
	if n > f.delta {
		n -= f.delta
		f.delta = 0
	} else {

View on GitHub (pinned to 0c51461d27)