hyperledger/fabric · error
expected ValidationPlugin '%s' does not match passed Validat
Error message
expected ValidationPlugin '%s' does not match passed ValidationPlugin '%s'
What it means
Thrown by ChaincodeParameters.Equal when the ValidationPlugin field of the new definition differs from the committed one. Lifecycle rejects changing the validation plugin (default 'vscc') for the same sequence number.
Source
Thrown at core/chaincode/lifecycle/lifecycle.go:132
// 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.
type ChaincodeDefinition struct {
Sequence int64
EndorsementInfo *lb.ChaincodeEndorsementInfo
ValidationInfo *lb.ChaincodeValidationInfoView on GitHub (pinned to 2736b63f8f)
Solutions
- Use the same ValidationPlugin as the committed definition, or increment the sequence number
- Confirm the plugin is registered in the peer's validators configuration
- Align all org approvals on one ValidationPlugin value before committing
Example fix
// before ValidationPlugin: 'custom-vscc', sequence: 1 // committed uses 'vscc' // after ValidationPlugin: 'vscc', sequence: 1 // or sequence: 2 to change plugins intentionally
Defensive patterns
Strategy: validation
Validate before calling
committed, err := lifecycle.QueryChaincodeDefinition(chname, ccname, publicState)
if err == nil && newDef.ValidationInfo.ValidationPlugin != committed.ValidationInfo.ValidationPlugin && newDef.Sequence == committed.Sequence {
return fmt.Errorf("validation plugin change requires sequence bump")
} Type guard
func sameValidationPlugin(a, b *lifecycle.ChaincodeParameters) bool {
return a != nil && b != nil && a.ValidationInfo.ValidationPlugin == b.ValidationInfo.ValidationPlugin
} Try / catch
if err := params.Equal(committedParams); err != nil {
if strings.Contains(err.Error(), "ValidationPlugin") {
// reuse committed plugin or bump sequence
}
return err
} Prevention
- Use the default 'vscc' unless a custom validator is intentionally deployed
- Confirm custom validator names exist in peer configuration before approval
- Keep approvals byte-identical across organizations
When it happens
Trigger: Committing/redefining a chaincode whose ValidationInfo.ValidationPlugin differs from the committed definition, via CheckCommitReadiness/CommitChaincodeDefinition comparison paths.
Common situations: Switching to a custom state-based validation plugin without incrementing sequence; mismatched approvals where orgs approved different plugin names.
Related errors
- expected EndorsementPlugin '%s' does not match passed Endors
- expected InitRequired '%t' does not match passed InitRequire
- chaincode definition for [%s] is invalid, plugin field must
- failed listing installed chaincodes
- failed to parse collection config
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/9fde5c80e4e79629.
Report an issue: GitHub.