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

  1. Retry the Step connection — the sender (Cluster/Dialer) will re-establish the stream automatically; the error propagates and triggers a reconnect with backoff.
  2. Upgrade all orderers to the same Fabric version so the Step stream protocol implementations match.
  3. Check network intermediaries (load balancers, Istio, NAT gateways) for idle-timeout or body-mangling behavior and set keepalives appropriately.
  4. 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

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


AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04). Data as JSON: /api/errors/aa1e35d010ac9744. Report an issue: GitHub.