hyperledger/fabric · error

error marshaling: proto: Marshal called with nil

Error message

error marshaling: proto: Marshal called with nil

What it means

CreateSignedEnvelopeWithTLSBinding explicitly rejects a dataMsg message that is a typed-nil or otherwise invalid protobuf message before calling proto.Marshal, returning 'error marshaling: proto: Marshal called with nil'. Passing a nil concrete message (e.g. (*common.Payload)(nil)) to proto.Marshal panics or errors, so the library guards and returns this sentinel error instead.

Source

Thrown at protoutil/txutils.go:98

	dataMsg proto.Message,
	msgVersion int32,
	epoch uint64,
	tlsCertHash []byte,
) (*common.Envelope, error) {
	payloadChannelHeader := MakeChannelHeader(txType, msgVersion, channelID, epoch)
	payloadChannelHeader.TlsCertHash = tlsCertHash
	var err error
	payloadSignatureHeader := &common.SignatureHeader{}

	if signer != nil {
		payloadSignatureHeader, err = NewSignatureHeader(signer)
		if err != nil {
			return nil, err
		}
	}

	if !dataMsg.ProtoReflect().IsValid() {
		return nil, errors.New("error marshaling: proto: Marshal called with nil")
	}
	data, err := proto.Marshal(dataMsg)
	if err != nil {
		return nil, errors.Wrap(err, "error marshaling")
	}

	paylBytes := MarshalOrPanic(
		&common.Payload{
			Header: MakePayloadHeader(payloadChannelHeader, payloadSignatureHeader),
			Data:   data,
		},
	)

	var sig []byte
	if signer != nil {
		sig, err = signer.Sign(paylBytes)
		if err != nil {
			return nil, err

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Check that the message passed to CreateSignedEnvelopeWithTLSBinding is non-nil before calling it
  2. Audit the code path that produced the message for an unchecked (nil, err) return from a constructor or unmarshal call
  3. Avoid typed-nil interface pitfalls: return untyped nil on error paths, or check with reflection/is-nil helper
  4. If the message can legitimately be absent, skip envelope creation instead of marshaling

Example fix

// before
env, _ := protoutil.CreateSignedEnvelopeWithTLSBinding(
    common.HeaderType_ENDORSER_TRANSACTION, chID, creator, payload, nil, 0)
// after
if payload == nil {
    return nil, fmt.Errorf("payload is nil")
}
env, err := protoutil.CreateSignedEnvelopeWithTLSBinding(
    common.HeaderType_ENDORSER_TRANSACTION, chID, creator, payload, nil, 0)
if err != nil {
    return nil, err
}
Defensive patterns

Strategy: validation

Validate before calling

func marshalGuard(msg proto.Message) error {
    if msg == nil || !msg.ProtoReflect().IsValid() {
        return fmt.Errorf("message is nil or invalid")
    }
    return nil
}

Type guard

func isNilProtoMsg(m interface{}) bool {
    if m == nil { return true }
    v := reflect.ValueOf(m)
    switch v.Kind() {
    case reflect.Ptr, reflect.Interface:
        return v.IsNil()
    }
    return false
}

Try / catch

if err := marshalGuard(dataMsg); err != nil {
    return nil, err
}
env, err := protoutil.CreateSignedEnvelopeWithTLSBinding(t, chID, signer, dataMsg, tlsCertHash, 0)
if err != nil {
    return nil, fmt.Errorf("createSignedEnvelope: %w", err)
}

Prevention

When it happens

Trigger: Calling CreateSignedEnvelopeWithTLSBinding (or the envelope helpers built on it: SeekInfoBlocksFrom, SeekInfoHeadersFrom, seekHelper, seekLastEnvelope, seekNextEnvelope, createDeliverEnvelope) with a nil typed message, e.g. nil *common.Payload, nil *SeekInfo, or nil *Envelope.

Common situations: A variable holding a typed-nil pointer because an earlier constructor returned (nil, err) and the err wasn't checked; Go interface-holding-typed-nil pitfalls when passing messages through interface{} parameters.

Related errors


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