hyperledger/fabric · error

unrecognized type, expected a principal or a policy, got %s

Error message

unrecognized type, expected a principal or a policy, got %s

What it means

secondPass in common/policydsl/policyparser.go:224 rejects an argument to an OutOf/And/Or gate that is neither a principal string nor a nested *cb.SignaturePolicy. The internal expression pipeline should only ever produce strings (principals like 'Org1.member') or previously built SignaturePolicy values; any other type reaching this switch means the generated expression was malformed or a function returned an unexpected type.

Source

Thrown at common/policydsl/policyparser.go:224

			/* create a SignaturePolicy that requires a signature from
			   the principal we've just built*/
			dapolicy := SignedBy(int32(ctx.IDNum))
			policies = append(policies, dapolicy)

			/* increment the identity counter. Note that this is
			   suboptimal as we are not reusing identities. We
			   can deduplicate them easily and make this puppy
			   smaller. For now it's fine though */
			// TODO: deduplicate principals
			ctx.IDNum++

		/* if we've already got a policy we're good, just append it */
		case *cb.SignaturePolicy:
			policies = append(policies, t)

		default:
			return nil, fmt.Errorf("unrecognized type, expected a principal or a policy, got %s", reflect.TypeOf(principal))
		}
	}

	return NOutOf(int32(t), policies), nil
}

type context struct {
	IDNum      int
	principals []*mb.MSPPrincipal
}

func newContext() *context {
	return &context{IDNum: 0, principals: make([]*mb.MSPPrincipal, 0)}
}

// FromString takes a string representation of the policy,
// parses it and returns a SignaturePolicyEnvelope that
// implements that policy. The supported language is as follows:

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Quote the argument so it parses as a principal string of the form '<MSP_ID>.<role>' where role is one of member|admin|client|peer|orderer, e.g. OutOf(1, 'Org1.member')
  2. Check for unbalanced or misplaced parentheses that shift arguments between nested gates
  3. Ensure every argument to And/Or/OutOf is either a quoted principal or a nested gate call, never a number or bare identifier
  4. Test the policy string through policydsl.FromString in a unit test before deploying it in channel or chaincode policy config

Example fix

// before
FromString("OutOf(1, Org1.member, 2)")
// after
FromString("OutOf(1, 'Org1.member', 'Org1.peer')")
Defensive patterns

Strategy: validation

Validate before calling

var principalRe = regexp.MustCompile(`^[[:alnum:].-]+[.](member|admin|client|peer|orderer)$`)

func validatePolicyArgs(policy string) error {
	if !strings.Contains(policy, "And(") && !strings.Contains(policy, "Or(") && !strings.Contains(policy, "OutOf(") &&
		!strings.Contains(policy, "and(") && !strings.Contains(policy, "or(") && !strings.Contains(policy, "outof(") {
		return fmt.Errorf("policy %q must be rooted in And/Or/OutOf", policy)
	}
	return nil
}
// plus: every non-gate argument must match principalRe

Try / catch

_, err := policydsl.FromString(policy)
if err != nil && strings.Contains(err.Error(), "unrecognized type, expected a principal or a policy") {
	return fmt.Errorf("policy %q has a non-principal argument in a gate: %w", policy, err)
}

Prevention

When it happens

Trigger: Calling policydsl.FromString with an expression argument inside a gate that is not a quoted principal and not a gate call, e.g. FromString("OutOf(1, Org1.member, 42)") or a bare identifier - expr passes it through and the switch's default branch fires.

Common situations: Typos in policy strings in channel configtx.yaml or policy definitions in chaincode (e.g. missing quotes around an MSP identifier, unbalanced parentheses causing arguments to shift, or passing numeric/naked tokens where 'OrgMSP.peer' style principals are expected).

Related errors


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