hyperledger/fabric · error
expected EndorsementPlugin '%s' does not match passed Endors
Error message
expected EndorsementPlugin '%s' does not match passed EndorsementPlugin '%s'
What it means
Thrown by ChaincodeParameters.Equal when the EndorsementPlugin field of the new chaincode definition differs from the committed one. Lifecycle forbids silently changing which endorsement plugin a chaincode uses between definitions at the same sequence.
Source
Thrown at core/chaincode/lifecycle/lifecycle.go:128
PackageID string
}
// ChaincodeParameters are the parts of the chaincode definition which are serialized
// as values in the statedb. It is expected that any instance will have no nil fields once initialized.
// WARNING: This structure is serialized/deserialized from the DB, re-ordering or adding fields
// will cause opaque checks to fail.
type ChaincodeParameters struct {
EndorsementInfo *lb.ChaincodeEndorsementInfo
ValidationInfo *lb.ChaincodeValidationInfo
Collections *pb.CollectionConfigPackage
}
func (cp *ChaincodeParameters) Equal(ocp *ChaincodeParameters) error {
switch {
case cp.EndorsementInfo.Version != ocp.EndorsementInfo.Version:
return errors.Errorf("expected Version '%s' does not match passed Version '%s'", cp.EndorsementInfo.Version, ocp.EndorsementInfo.Version)
case cp.EndorsementInfo.EndorsementPlugin != ocp.EndorsementInfo.EndorsementPlugin:
return errors.Errorf("expected EndorsementPlugin '%s' does not match passed EndorsementPlugin '%s'", cp.EndorsementInfo.EndorsementPlugin, ocp.EndorsementInfo.EndorsementPlugin)
case cp.EndorsementInfo.InitRequired != ocp.EndorsementInfo.InitRequired:
return errors.Errorf("expected InitRequired '%t' does not match passed InitRequired '%t'", cp.EndorsementInfo.InitRequired, ocp.EndorsementInfo.InitRequired)
case cp.ValidationInfo.ValidationPlugin != ocp.ValidationInfo.ValidationPlugin:
return errors.Errorf("expected ValidationPlugin '%s' does not match passed ValidationPlugin '%s'", cp.ValidationInfo.ValidationPlugin, ocp.ValidationInfo.ValidationPlugin)
case !bytes.Equal(cp.ValidationInfo.ValidationParameter, ocp.ValidationInfo.ValidationParameter):
return errors.Errorf("expected ValidationParameter '%x' does not match passed ValidationParameter '%x'", cp.ValidationInfo.ValidationParameter, ocp.ValidationInfo.ValidationParameter)
case !proto.Equal(cp.Collections, ocp.Collections):
return errors.Errorf("Collections do not match")
default:
}
return nil
}
// ChaincodeDefinition contains the chaincode parameters, as well as the sequence number of the definition.
// Note, it does not embed ChaincodeParameters so as not to complicate the serialization. It is expected
// that any instance will have no nil fields once initialized.
// WARNING: This structure is serialized/deserialized from the DB, re-ordering or adding fields
// will cause opaque checks to fail.View on GitHub (pinned to 2736b63f8f)
Solutions
- Keep the EndorsementPlugin identical to the committed definition, or increase the sequence number to intentionally change it
- Verify the plugin name spelling against the peer's core.yaml endorsementPlugins configuration
- Re-approve the definition on all orgs with a consistent EndorsementPlugin value
Example fix
// before EndorsementPlugin: 'my-custom-escc', sequence: 1 // committed uses 'escc' // after EndorsementPlugin: 'escc', sequence: 1 // or sequence: 2 to legitimately change plugins
Defensive patterns
Strategy: validation
Validate before calling
committed, err := lifecycle.QueryChaincodeDefinition(chname, ccname, publicState)
if err == nil && newDef.EndorsementInfo.EndorsementPlugin != committed.EndorsementInfo.EndorsementPlugin && newDef.Sequence == committed.Sequence {
return fmt.Errorf("endorsement plugin change requires sequence bump")
} Type guard
func sameEndorsementPlugin(a, b *lifecycle.ChaincodeParameters) bool {
return a != nil && b != nil && a.EndorsementInfo.EndorsementPlugin == b.EndorsementInfo.EndorsementPlugin
} Try / catch
if err := params.Equal(committedParams); err != nil {
var expected string
if strings.Contains(err.Error(), "EndorsementPlugin") {
// fall back to committed plugin name or bump sequence
}
return err
} Prevention
- Stick to the default 'escc' unless a custom plugin is deliberately deployed
- Verify custom plugin names are registered in peer core.yaml before approval
- Use identical approval payloads across all organizations
When it happens
Trigger: Committing or redefining a chaincode whose EndorsementInfo.EndorsementPlugin differs from the already-committed definition; comparison invoked from isAttemptToRedefine or CommitChaincodeDefinition.
Common situations: Switching from the default 'escc' to a custom endorsement plugin without bumping sequence; one org approved with 'escc' while another approved a different plugin name and the reconciled definition differs.
Related errors
- expected InitRequired '%t' does not match passed InitRequire
- expected ValidationPlugin '%s' does not match passed Validat
- failed listing installed chaincodes
- failed to parse collection config
- [channel %s] failed to get chaincode container info for %s
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/b8d8562bd58880f1.
Report an issue: GitHub.