hyperledger/fabric · error

No principals in CombinedPrincipal

Error message

No principals in CombinedPrincipal

What it means

A COMBINED MSPPrincipal successfully unmarshaled but contains zero nested principals, so collectPrincipals returns 'No principals in CombinedPrincipal'. An empty AND-group can never be satisfied meaningfully, so the library treats it as malformed policy input rather than 'always true' or 'always false'.

Source

Thrown at msp/mspimpl.go:464

}

// collectPrincipals collects principals from combined principals into a single MSPPrincipal slice.
func collectPrincipals(principal *m.MSPPrincipal, mspVersion MSPVersion) ([]*m.MSPPrincipal, error) {
	switch principal.PrincipalClassification {
	case m.MSPPrincipal_COMBINED:
		// Combined principals are not supported in MSP v1.0 or v1.1
		if mspVersion <= MSPv1_1 {
			return nil, errors.Errorf("invalid principal type %d", int32(principal.PrincipalClassification))
		}
		// Principal is a combination of multiple principals.
		principals := &m.CombinedPrincipal{}
		err := proto.Unmarshal(principal.Principal, principals)
		if err != nil {
			return nil, errors.Wrap(err, "could not unmarshal CombinedPrincipal from principal")
		}
		// Return an error if there are no principals in the combined principal.
		if len(principals.Principals) == 0 {
			return nil, errors.New("No principals in CombinedPrincipal")
		}
		// Recursively call msp.collectPrincipals for all combined principals.
		// There is no limit for the levels of nesting for the combined principals.
		var principalsSlice []*m.MSPPrincipal
		for _, cp := range principals.Principals {
			internalSlice, err := collectPrincipals(cp, mspVersion)
			if err != nil {
				return nil, err
			}
			principalsSlice = append(principalsSlice, internalSlice...)
		}
		// All the combined principals have been collected into principalsSlice
		return principalsSlice, nil
	default:
		return []*m.MSPPrincipal{principal}, nil
	}
}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Ensure the CombinedPrincipal has at least one nested MSPPrincipal before marshaling and submitting the policy
  2. Check upstream policy-building code/config for empty org/role lists that silently produce an empty CombinedPrincipal
  3. If an empty group is semantically expected, restructure the policy (drop the combined wrapper or use an implicit meta policy)

Example fix

// before
principal, _ := proto.Marshal(&m.CombinedPrincipal{}) // empty -> error
// after
if len(subPrincipals) == 0 {
    return errors.New("cannot build CombinedPrincipal with no principals")
}
principal, _ := proto.Marshal(&m.CombinedPrincipal{Principals: subPrincipals})
Defensive patterns

Strategy: validation

Validate before calling

cp := &m.CombinedPrincipal{}
if err := proto.Unmarshal(principal.Principal, cp); err != nil {
    return err
}
if len(cp.Principals) == 0 {
    return errors.New("CombinedPrincipal has no nested principals")
}
// safe to call SatisfiesPrincipal

Type guard

func hasNestedPrincipals(b []byte) bool {
    cp := &m.CombinedPrincipal{}
    if proto.Unmarshal(b, cp) != nil {
        return false
    }
    return len(cp.Principals) > 0
}

Try / catch

ok, err := msp.SatisfiesPrincipal(id, principal)
if err != nil {
    if err.Error() == "No principals in CombinedPrincipal" {
        // policy was built with an empty sub-principal list; rebuild the policy
    }
    return err
}

Prevention

When it happens

Trigger: Calling SatisfiesPrincipal/collectPrincipals with a COMBINED principal built from an empty principals list — e.g. an SDK policy builder given no sub-policies, a policy template never filled in, or a CombinedPrincipal constructed as &m.CombinedPrincipal{} and marshaled directly.

Common situations: Policy generation code paths where a list of organizations/roles was empty (missing config), default/template policies shipped unfilled, or tests intentionally constructing empty combined principals (TestCollectEmptyCombinedPrincipal) to assert this error.

Related errors


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