hyperledger/fabric · error

proto: Marshal called with nil

Error message

proto: Marshal called with nil

What it means

The lifecycle serializer's Marshaler wrapper checks whether the proto message is valid (non-nil) before calling proto.Marshal; calling Marshal with a nil/invalid proto.Message yields this sentinel error instead of panicking inside the protobuf library.

Source

Thrown at core/chaincode/lifecycle/serializer.go:56

}

type RangeableState interface {
	GetStateRange(prefix string) (map[string][]byte, error)
}

type ReadRangeableState interface {
	ReadableState
	RangeableState
}

type Marshaler func(proto.Message) ([]byte, error)

func (m Marshaler) Marshal(msg proto.Message) ([]byte, error) {
	if m != nil {
		return m(msg)
	}
	if !msg.ProtoReflect().IsValid() {
		return nil, errors.New("proto: Marshal called with nil")
	}
	return proto.Marshal(msg)
}

var ProtoMessageType = reflect.TypeFor[proto.Message]()

// Serializer is used to write structures into the db and to read them back out.
// Although it's unfortunate to write a custom serializer, rather than to use something
// pre-written, like protobuf or JSON, in order to produce precise readwrite sets which
// only perform state updates for keys which are actually updated (and not simply set
// to the same value again) custom serialization is required.
type Serializer struct {
	// Marshaler, when nil uses the standard protobuf impl.
	// Can be overridden for test.
	Marshaler Marshaler
}

// SerializableChecks performs some boilerplate checks to make sure the given structure

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Initialize the proto pointer field before serialization (allocate the concrete message struct)
  2. Check field validity with msg.ProtoReflect().IsValid() before calling Marshal
  3. Skip/omit nil proto fields — Serialize already guards with fieldValue.IsNil(), so ensure that path is used rather than passing nil directly

Example fix

// before
var policy pb.ApplicationPolicy
bytes, err := m.Marshal(&policy) // invalid message
// after
policy := &pb.ApplicationPolicy{Type: &pb.ApplicationPolicy_ChannelConfigPolicyReference{ChannelConfigPolicyReference: "admin"}}
bytes, err := m.Marshal(policy)
Defensive patterns

Strategy: type-guard

Validate before calling

if msg == nil || !msg.ProtoReflect().IsValid() {
  return nil, errors.New("message is nil/invalid before Marshal")
}

Type guard

func isValidProto(msg proto.Message) bool { return msg != nil && msg.ProtoReflect().IsValid() }

Try / catch

bytes, err := m.Marshal(msg)
if err != nil && strings.Contains(err.Error(), "Marshal called with nil") {
  // initialize the message before retrying
}

Prevention

When it happens

Trigger: Serialize or metadata/serialization checks in _lifecycle invoking Marshaler.Marshal with a nil proto.Message pointer (e.g. a nil *lb.ChaincodeDefinition embedded field), typically due to a nil field in the struct being serialized.

Common situations: Programmatic use of lifecycle serializer with structs whose proto pointer fields were never initialized; a nil EndorsementPolicy or ValidationParameter pointer reaching Serialize.

Related errors


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