hyperledger/fabric · error

proto: Marshal called with nil

Error message

proto: Marshal called with nil

What it means

putChaincodeData rejects a nil *ccprovider.ChaincodeData before attempting proto.Marshal, returning the same message the protobuf library uses for nil messages. LSCC requires a valid ChaincodeData to persist the chaincode definition to the ledger; passing nil is a programming error in the caller (e.g., executeChaincode/GetChaincodeToInstall flow constructed no chaincode data).

Source

Thrown at core/scc/lscc/lscc.go:327

	chaincodeData := &ccprovider.ChaincodeData{}
	err = proto.Unmarshal(chaincodeDataBytes, chaincodeData)
	if err != nil {
		// this kind of data corruption is unexpected since our code
		// always marshals ChaincodeData into these keys
		unexpectedErr = errors.Wrapf(err, "chaincode %s has bad definition", chaincodeName)
		return
	}

	plugin = chaincodeData.Vscc
	args = chaincodeData.Policy
	return
}

// create the chaincode on the given chain
func (lscc *SCC) putChaincodeData(stub shim.ChaincodeStubInterface, cd *ccprovider.ChaincodeData) error {
	if cd == nil {
		return errors.New("proto: Marshal called with nil")
	}
	cdbytes, err := proto.Marshal(cd)
	if err != nil {
		return err
	}

	if cdbytes == nil {
		return MarshallErr(cd.Name)
	}

	err = stub.PutState(cd.Name, cdbytes)

	return err
}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Ensure the ChaincodeData passed to putChaincodeData is fully populated (name, version, path, Vscc, Policy, Escc, Id) before persisting.
  2. Check the caller that builds the ChaincodeData for early error returns that leave it nil; propagate those errors instead of continuing.
  3. If patching/forking lscc, add the nil check (as upstream does) so the failure is explicit rather than a proto.Marshal panic-style error.

Example fix

// before
cd := buildChaincodeData(spec) // may return nil on parse failure
lscc.putChaincodeData(stub, cd)
// after
cd := buildChaincodeData(spec)
if cd == nil {
    return fmt.Errorf("failed to build ChaincodeData for %s", spec.ChaincodeSpec.ChaincodeId.Name)
}
lscc.putChaincodeData(stub, cd)
Defensive patterns

Strategy: validation

Validate before calling

// guard the ChaincodeData before the LSCC persist path
func validChaincodeData(cd *ccprovider.ChaincodeData) bool {
    return cd != nil && cd.Name != "" && cd.Version != "" && cd.Vscc != "" && cd.Escc != ""
}

Type guard

func isNilChaincodeData(cd *ccprovider.ChaincodeData) bool { return cd == nil || reflect.ValueOf(cd).IsNil() }

Try / catch

// Go: check error from the deploy path and report nil-data programming bugs
if err := lscc.Invoke(stub); err != nil {
    if strings.Contains(err.Error(), "Marshal called with nil") {
        return fmt.Errorf("internal: ChaincodeData was nil before persist: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling lscc.putChaincodeData (via LSCC deploy/instantiate processing) with cd == nil — typically when a deploy path fails to build a ChaincodeData from the deployment spec before persisting it.

Common situations: Custom lifecycle code or forks of lscc that construct ChaincodeData conditionally and skip error checks; upstream bug paths where an earlier parse of the ChaincodeDeploymentSpec returned no data but the flow continued to putChaincodeData.

Related errors


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