hyperledger/fabric · warning
request message is nil
Error message
request message is nil
What it means
handleMessage reads the next request from the ClusterStepStream. If Recv returns no error but the request is nil, the stream handed back an empty message, which is not a valid Submit/Consensus request. The service treats this as a protocol violation and closes the stream with 'request message is nil'.
Source
Thrown at orderer/common/cluster/clusterservice.go:193
err = VerifySignature(fromIdentity, SHA256Digest(msg), authReq.Signature)
if err != nil {
return nil, errors.Wrap(err, "signature mismatch")
}
return authReq, nil
}
func (s *ClusterService) handleMessage(stream ClusterStepStream, addr string, exp *certificateExpirationCheck, channel string, sender uint64, streamID uint64) error {
request, err := stream.Recv()
if err == io.EOF {
return err
}
if err != nil {
s.Logger.Warningf("Stream read from %s failed: %v", addr, err)
return err
}
if request == nil {
return errors.Errorf("request message is nil")
}
s.Lock.RLock()
_, authorized := s.MembershipByChannel[channel].AuthorizedStreams.Load(streamID)
s.Lock.RUnlock()
if !authorized {
return errors.Errorf("stream %d is stale", streamID)
}
if s.StepLogger.IsEnabledFor(zap.DebugLevel) {
nodeName := commonNameFromContext(stream.Context())
s.StepLogger.Debugf("Received message from %s(%s): %v", nodeName, addr, clusterRequestAsString(request))
}
exp.checkExpiration(time.Now(), channel)
if tranReq := request.GetNodeTranrequest(); tranReq != nil {View on GitHub (pinned to 2736b63f8f)
Solutions
- Retry the Step connection — the sender (Cluster/Dialer) will re-establish the stream automatically; the error propagates and triggers a reconnect with backoff.
- Upgrade all orderers to the same Fabric version so the Step stream protocol implementations match.
- Check network intermediaries (load balancers, Istio, NAT gateways) for idle-timeout or body-mangling behavior and set keepalives appropriately.
- Enable StepLogger debug on both sides to see whether the sender actually transmitted a message before the failure.
Defensive patterns
Strategy: retry
Try / catch
err := handleMessage(stream, addr, exp, channel, sender, streamID)
if err != nil && strings.Contains(err.Error(), "request message is nil") {
// transient/empty frame: tear down and let the dialer reconnect
stream.CloseSend()
time.Sleep(backoff)
return retryStep(channel, endpoint)
} Prevention
- Enable gRPC keepalives on both orderer sides to avoid half-open streams
- Keep all orderer nodes on the same Fabric version
- Check load balancers/service meshes for idle-timeout stream resets
- Monitor Step stream error rates to catch flaky network paths early
When it happens
Trigger: Step's stream Recv loop obtains err == nil with request == nil — i.e. the remote side sent an empty/void message on the orderer.ClusterNodeService/Step gRPC stream. This can arise from a peer implementation bug, a half-open connection that delivered an empty frame, or a client that opened the stream and sent an empty envelope.
Common situations: Network gear or proxies cutting the gRPC stream mid-use leading to empty reads; a non-Fabric or older-version client speaking to the Step endpoint incorrectly; transient connection drops during cluster membership churn causing Recv to surface a nil message with nil error.
Related errors
- Recv() error: %v, closing connection
- got unexpected status: %v -- %s
- stream %d is stale
- Message is neither a Submit nor Consensus request
- badly formatted message, cannot extract channel
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/aa1e35d010ac9744.
Report an issue: GitHub.