hyperledger/fabric · error

signature policy is not an OR concatenation, NOutOf %d

Error message

signature policy is not an OR concatenation, NOutOf %d

What it means

validateSpOrConcat enforces that a collection's MemberOrgsPolicy signature policy is only a concatenation of OR rules (any-of semantics, NOutOf with N==1, possibly nested). If any NOutOf node in the policy tree has N != 1 (e.g. a 2-of-3 rule), the policy is rejected because collection membership policies must express 'any of these organizations'.

Source

Thrown at core/handlers/validation/builtin/v13/lscc_validation_logic.go:110

		}

		// make sure that the signature policy is meaningful (only consists of ORs)
		err := validateSpOrConcat(newCollection.MemberOrgsPolicy.GetSignaturePolicy().Rule)
		if err != nil {
			return errors.WithMessagef(err, "collection-name: %s -- error in member org policy", collectionName)
		}
	}
	return nil
}

// validateSpOrConcat checks if the supplied signature policy is just an OR-concatenation of identities
func validateSpOrConcat(sp *common.SignaturePolicy) error {
	if sp.GetNOutOf() == nil {
		return nil
	}
	// check if N == 1 (OR concatenation)
	if sp.GetNOutOf().N != 1 {
		return errors.New(fmt.Sprintf("signature policy is not an OR concatenation, NOutOf %d", sp.GetNOutOf().N))
	}
	// recurse into all sub-rules
	for _, rule := range sp.GetNOutOf().Rules {
		err := validateSpOrConcat(rule)
		if err != nil {
			return err
		}
	}
	return nil
}

func checkForMissingCollections(newCollectionsMap map[string]*pb.StaticCollectionConfig, oldCollectionConfigs []*pb.CollectionConfig,
) error {
	var missingCollections []string

	// In the new collection config package, ensure that there is one entry per old collection. Any
	// number of new collections are allowed.
	for _, oldCollectionConfig := range oldCollectionConfigs {

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Rewrite the member orgs policy so every NOutOf node has N==1, listing organizations as OR-ed principals
  2. Use the collections config helpers so the policy is built as an OR concatenation of the desired org MSP principals
  3. Do not reuse chaincode endorsement policies verbatim as collection member policies; only the 'any org' subset is allowed

Example fix

// before
policy := &common.SignaturePolicy{Type: &common.SignaturePolicy_NOutOf{NOutOf: &common.SignaturePolicy_NOutOf{N: 2, Rules: rules}}}
// after
policy := &common.SignaturePolicy{Type: &common.SignaturePolicy_NOutOf{NOutOf: &common.SignaturePolicy_NOutOf{N: 1, Rules: rules}}}
Defensive patterns

Strategy: validation

Validate before calling

function validateMemberOrgsPolicy(policy) {
  const walk = (sp) => {
    if (sp.nOutOf) {
      if (sp.nOutOf.n !== 1) throw new Error('member orgs policy must be an OR concatenation (nOutOf n=1)');
      sp.nOutOf.rules.forEach(walk);
    }
  };
  walk(policy);
}

Type guard

function isOrConcatPolicy(sp) {
  if (!sp || !sp.nOutOf) return true;
  return sp.nOutOf.n === 1 && sp.nOutOf.rules.every(isOrConcatPolicy);
}

Try / catch

try {
  await contract.submitTransaction('DeployChaincode', ...args);
} catch (err) {
  if (String(err).includes('not an OR concatenation')) {
    // rebuild memberOrgsPolicy with n=1 rules
  }
  throw err;
}

Prevention

When it happens

Trigger: Defining a collection whose memberOrgsPolicy signaturePolicy contains an NOutOf rule with N > 1 (or effectively 0) anywhere in the tree, submitted as part of a collection config during chaincode definition.

Common situations: Building the SignaturePolicyEnvelope programmatically with common.SignaturePolicy_NOutOf{N: 2, ...}; converting an endorsement policy (which often uses N-of semantics) and reusing it as a collection member policy; policy YAML/JSON with 'signedBy' counts.

Related errors


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