hyperledger/fabric · error

chaincode spec is nil

Error message

chaincode spec is nil

What it means

InvokedChaincodeName parses a SignedProposal's payload into a ChaincodeInvocationSpec and returns the invoked chaincode's name. After unmarshaling the proposal payload, if the ChaincodeInvocationSpec has a nil ChaincodeSpec there is no invoked chaincode to identify, so the function returns this error.

Source

Thrown at protoutil/proputils.go:431

	err := proto.Unmarshal(proposalBytes, proposal)
	if err != nil {
		return "", errors.WithMessage(err, "could not unmarshal proposal")
	}

	proposalPayload := &peer.ChaincodeProposalPayload{}
	err = proto.Unmarshal(proposal.Payload, proposalPayload)
	if err != nil {
		return "", errors.WithMessage(err, "could not unmarshal chaincode proposal payload")
	}

	cis := &peer.ChaincodeInvocationSpec{}
	err = proto.Unmarshal(proposalPayload.Input, cis)
	if err != nil {
		return "", errors.WithMessage(err, "could not unmarshal chaincode invocation spec")
	}

	if cis.ChaincodeSpec == nil {
		return "", errors.Errorf("chaincode spec is nil")
	}

	if cis.ChaincodeSpec.ChaincodeId == nil {
		return "", errors.Errorf("chaincode id is nil")
	}

	return cis.ChaincodeSpec.ChaincodeId.Name, nil
}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Populate the ChaincodeInvocationSpec's ChaincodeSpec (including ChaincodeId and Input) before signing and sending the proposal.
  2. Validate the proposal construction path in your SDK/CLI so cis.ChaincodeSpec is always set for invoke proposals.
  3. If the proposal came from the wire, treat it as malformed and reject it; re-fetch or have the client rebuild the transaction.
  4. Check proto marshaling of the upstream proposal — ensure the right message type was serialized into Payload.Input.

Example fix

// before
cis := &peer.ChaincodeInvocationSpec{}

// after
cis := &peer.ChaincodeInvocationSpec{
    ChaincodeSpec: &peer.ChaincodeSpec{
        Type:        peer.ChaincodeSpec_NODE,
        ChaincodeId: &peer.ChaincodeID{Name: "mycc"},
        Input:       &peer.ChaincodeInput{Args: args},
    },
}
Defensive patterns

Strategy: validation

Validate before calling

var cis peer.ChaincodeInvocationSpec
if err := proto.Unmarshal(proposalPayload.Input, &cis); err != nil {
    return err
}
if cis.ChaincodeSpec == nil {
    return errors.New("proposal has no ChaincodeSpec")
}

Type guard

func hasChaincodeSpec(cis *peer.ChaincodeInvocationSpec) bool {
    return cis != nil && cis.ChaincodeSpec != nil
}

Try / catch

name, err := protoutil.InvokedChaincodeName(signedProp)
if err != nil {
    return fmt.Errorf("malformed proposal (no chaincode spec): %w", err)
}

Prevention

When it happens

Trigger: Submitting a SignedProposal whose payload.Input unmarshals to a ChaincodeInvocationSpec with no ChaincodeSpec set — e.g. a proposal built with an empty ChaincodeInvocationSpec or with only the decorator/args fields left unset.

Common situations: Manually assembled proposals in tests missing ChaincodeSpec; corrupted or truncated payload bytes that decode but leave fields empty; SDK calls that send an invoke proposal with an unset spec after failed construction.

Related errors


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