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.ChaincodeValidationInfo

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Use the same ValidationPlugin as the committed definition, or increment the sequence number
  2. Confirm the plugin is registered in the peer's validators configuration
  3. 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

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


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