hyperledger/fabric · critical

proto: Marshal called with nil

Error message

proto: Marshal called with nil

What it means

MarshalOrPanic serializes a protobuf message and panics on failure. Before marshaling it checks pb.ProtoReflect().IsValid(); a message holding a nil concrete pointer (typed nil) is invalid, and calling proto.Marshal on it panics inside the protobuf runtime. The library converts that into an explicit panic with the message 'proto: Marshal called with nil' so the failure is attributable to this helper.

Source

Thrown at protoutil/commonutils.go:24

package protoutil

import (
	"crypto/rand"
	"fmt"

	cb "github.com/hyperledger/fabric-protos-go-apiv2/common"
	"github.com/hyperledger/fabric/internal/pkg/identity"
	"github.com/pkg/errors"
	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/types/known/timestamppb"
)

// MarshalOrPanic serializes a protobuf message and panics if this
// operation fails
func MarshalOrPanic(pb proto.Message) []byte {
	if !pb.ProtoReflect().IsValid() {
		panic(errors.New("proto: Marshal called with nil"))
	}
	data, err := proto.Marshal(pb)
	if err != nil {
		panic(err)
	}
	return data
}

// Marshal serializes a protobuf message.
func Marshal(pb proto.Message) ([]byte, error) {
	if !pb.ProtoReflect().IsValid() {
		return nil, errors.New("proto: Marshal called with nil")
	}
	return proto.Marshal(pb)
}

// CreateNonceOrPanic generates a nonce using the common/crypto package
// and panics if this operation fails.

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Check the variable passed to MarshalOrPanic for nil before the call: if pb == nil { ... }.
  2. Audit the function that produced the message — handle its error return instead of ignoring it.
  3. Switch to protoutil.Marshal (non-panicking) at call sites where nil is plausible.
  4. Add a guard/helper that logs and returns an error instead of panicking in production paths.

Example fix

// before
raw := protoutil.MarshalOrPanic(env) // env may be nil
// after
if env == nil {
	return errors.New("envelope is nil")
}
raw, err := protoutil.Marshal(env)
if err != nil { return err }
Defensive patterns

Strategy: type-guard

Validate before calling

if env == nil {
	return errors.New("cannot marshal nil envelope")
}
raw := protoutil.MarshalOrPanic(env)

Type guard

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

Try / catch

func safeMarshal(pb proto.Message) (raw []byte, err error) {
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("marshal panicked: %v", r)
		}
	}()
	raw = protoutil.MarshalOrPanic(pb)
	return
}

Prevention

When it happens

Trigger: Calling MarshalOrPanic with a typed-nil message, e.g. protoutil.MarshalOrPanic((*cb.Envelope)(nil)) or a function that returned a nil *Envelope with a non-nil error ignored by the caller.

Common situations: serializeIdentity or block-building helpers receiving results of failed constructors whose errors were ignored; zero-value struct pointers in config-generation code; refactors where a factory started returning nil on an error path.

Related errors


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