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

Collection member orgs policies must be a flat OR concatenation of principals: every NOutOf rule must have N == 1 (an OR). This error is thrown when validateSpOrConcat encounters an NOutOf rule whose N is not 1, meaning the policy requires multiple principals jointly rather than any-one-of them.

Source

Thrown at core/chaincode/lifecycle/scc.go:908

			if _, err := mspMgr.DeserializeIdentity(principal.Principal); err != nil {
				return errors.Errorf("collection-name: %s -- contains an identity that is not part of the channel", coll.GetName())
			}

		default:
			return errors.Errorf("collection-name: %s -- principal type %v is not supported", coll.GetName(), principal.PrincipalClassification)
		}
	}
	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.Errorf("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 validateCollConfigsAgainstCommittedDef(
	proposedCollConfs []*pb.StaticCollectionConfig,
	committedCollConfPkg *pb.CollectionConfigPackage,
) error {
	if committedCollConfPkg == nil || len(committedCollConfPkg.Config) == 0 {
		return nil
	}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Rewrite the policy so all branches are NOutOf with N=1 (pure OR of principals).
  2. Express per-org requirements as separate principals in the OR list rather than AND-combining them.
  3. If AND semantics are required, move that requirement into chaincode-level checks, not the collection policy.
  4. Use a policy builder that produces OR-of-identities for collections.

Example fix

// before: AND of two orgs (N=2)
nOutOf := &common.SignaturePolicy_NOutOf{N: 2, Rules: rulesOfOrg1AndOrg2}
// after: OR of both orgs (N=1)
nOutOf := &common.SignaturePolicy_NOutOf{N: 1, Rules: []*common.SignaturePolicy{org1Member, org2Member}}
Defensive patterns

Strategy: validation

Validate before calling

func assertORConcat(sp *common.SignaturePolicy) error {
  if sp.GetNOutOf() == nil { return nil }
  if sp.GetNOutOf().N != 1 { return fmt.Errorf("NOutOf %d not allowed in collection policy", sp.GetNOutOf().N) }
  for _, r := range sp.GetNOutOf().Rules {
    if err := assertORConcat(r); err != nil { return err }
  }
  return nil
}

Type guard

func isORConcat(sp *common.SignaturePolicy) bool {
  if sp.GetNOutOf() == nil { return true }
  if sp.GetNOutOf().N != 1 { return false }
  for _, r := range sp.GetNOutOf().Rules {
    if !isORConcat(r) { return false }
  }
  return true
}

Try / catch

if err := approve(...); err != nil {
  if strings.Contains(err.Error(), "signature policy is not an OR concatenation") {
    // rewrite policy as OR of principals (N=1) and resubmit
  }
  return err
}

Prevention

When it happens

Trigger: A collection config member_orgs_policy signature policy containing NOutOf with N >= 2 (e.g. AND of two orgs or a 2-of-3 rule), during collection config validation.

Common situations: Copying a chaincode endorsement policy (which allows M-of-N) into a collection member orgs policy; expressing 'both orgs must agree' requirements in private data distribution, which fabric does not support.

Related errors


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