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
- Capture the error's reported limit and received bytes; if received is only slightly over, suspect a race or a peer accounting bug.
- Update the peer's HTTP/2/gRPC stack to a conformant version, or replace a misbehaving intermediary (proxy/LB).
- 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).
- 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
- Upgrade/replace peers or intermediaries that violate HTTP/2 flow control.
- Drain streams promptly so window updates flow back to the sender.
- Monitor channelz for per-connection flow-control errors.
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
- received an illegal stream id
- transport: timeout string is too long
- transport: timeout string is too short
- transport: timeout unit is not recognized
- ErrCodeEnhanceYourCalm
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)