grpc/grpc-go · error
received %d-bytes data exceeding the limit %d bytes
Error message
received %d-bytes data exceeding the limit %d bytes
What it means
This error occurs in inFlow.onData when the total received data (pendingData + pendingUpdate) exceeds the flow control limit plus any delta adjustment. It represents an HTTP/2 flow control violation: the peer sent more data than the receiver's advertised window allows. The stream is then closed with RST_STREAM (FLOW_CONTROL).
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 03255a9237)
Solutions
- Verify the client is a compliant gRPC/HTTP2 implementation that respects flow control windows.
- If streaming large messages, ensure the receiving side reads data promptly to avoid window exhaustion.
- Check for version mismatches between client and server gRPC libraries that might have flow control bugs.
- If you control both sides, consider increasing InitialWindowSize via dial/server options for large payloads.
Example fix
// before (broken): receiver doesn't read stream, sender floods data
stream, _ := client.LargeData(ctx)
// ... sender writes 100MB while receiver never calls stream.Recv()
// after (valid): receiver reads promptly to drain the flow control window
for {
resp, err := stream.Recv()
if err == io.EOF { break }
if err != nil { log.Fatal(err) }
// process resp incrementally
} Defensive patterns
Strategy: validation
Try / catch
// Server-side: when a stream is closed with FLOW_CONTROL error, the stream
// returns an error to the handler. Check for ResourceExhausted:
if status.Code(err) == codes.ResourceExhausted {
log.Printf("peer violated flow control: %v", err)
// the stream is already terminated; no recovery possible
} Prevention
- Always read from streaming RPCs promptly to drain the flow control window.
- Use a compliant gRPC/HTTP2 library on both sides.
- For large messages, consider chunking or increasing InitialWindowSize on both client and server.
- Monitor for RST_STREAM with FLOW_CONTROL in channelz/logs to detect misbehaving peers.
When it happens
Trigger: A gRPC peer sends a DATA frame that pushes the cumulative unconsumed data past the stream's flow control window. This can happen when the receiver is slow to read and the sender's window accounting is buggy, or when a non-compliant client ignores window limits.
Common situations: Client sends a very large message faster than the server reads it and a buggy client implementation doesn't respect flow control; or mismatched initial window size settings between client and server causing accounting discrepancies; or a malicious/buggy peer intentionally flooding data.
Related errors
- transport: timeout string is too short: %q
- transport: timeout string is too long: %q
- transport: timeout unit is not recognized: %q
- received an illegal stream id: %v. headers frame: %+v
- keepalive ping not acked within timeout %s
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/4c5e4cc700e0db39.
Report an issue: GitHub.