hyperledger/fabric · error

invalid policy string '%s'

Error message

invalid policy string '%s'

What it means

FromString (common/policydsl/policyparser.go:290) compiles and runs the policy DSL through the expr expression evaluator, whose gate functions (and/or/outof) build an intermediate string like 'outof(...)'. After the first evaluation the result must be a string; if expr returns a non-string value (e.g. a boolean from a comparison-only expression, or nothing at all), the parser gives up with 'invalid policy string'. It signals that the input is not a well-formed policy expression at all, even though it may be syntactically valid to expr.

Source

Thrown at common/policydsl/policyparser.go:290

	if err != nil {
		return nil, err
	}

	intermediateRes, err := expr.Run(intermediate, env)
	if err != nil {
		// attempt to produce a meaningful error
		if regexErr.MatchString(err.Error()) {
			sm := regexErr.FindStringSubmatch(err.Error())
			if len(sm) == 2 {
				return nil, fmt.Errorf("unrecognized token '%s' in policy string", sm[1])
			}
		}

		return nil, err
	}
	resStr, ok := intermediateRes.(string)
	if !ok {
		return nil, fmt.Errorf("invalid policy string '%s'", policy)
	}

	// we still need two passes. The first pass just adds an extra
	// argument ID to each of the outof calls. This is
	// required because govaluate has no means of giving context
	// to user-implemented functions other than via arguments.
	// We need this argument because we need a global place where
	// we put the identities that the policy requires
	env = map[string]interface{}{
		"outof": firstPass,
	}
	exp, err := expr.Compile(resStr, expr.Env(env))
	if err != nil {
		return nil, err
	}
	res, err := expr.Run(exp, env)
	if err != nil {
		// attempt to produce a meaningful error

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Wrap the policy in a valid gate call: every policy must have And/Or/OutOf at its root, e.g. OutOf(1, 'Org1.member', 'Org2.member')
  2. Ensure the policy string is non-empty and contains no bare expressions that evaluate to booleans/numbers
  3. Quote every principal argument ('Org1.member') so expr treats it as a string literal for the gate functions
  4. Test the string with policydsl.FromString before embedding it in channel/chaincode policy configuration

Example fix

// before
FromString("Org1.member") // no gate -> result is not a string
// after
FromString("OutOf(1, 'Org1.member', 'Org1.admin')")
Defensive patterns

Strategy: validation

Validate before calling

func validatePolicyString(policy string) error {
	trimmed := strings.TrimSpace(policy)
	if trimmed == "" {
		return fmt.Errorf("policy string is empty")
	}
	gateRe := regexp.MustCompile(`(?i)^(and|or|outof)\s*\(`)
	if !gateRe.MatchString(trimmed) {
		return fmt.Errorf("policy %q must start with an And/Or/OutOf call", policy)
	}
	return nil
}

Try / catch

_, err := policydsl.FromString(policy)
if err != nil && strings.Contains(err.Error(), "invalid policy string") {
	return fmt.Errorf("policy %q is not a gate expression; wrap principals in And/Or/OutOf: %w", policy, err)
}

Prevention

When it happens

Trigger: Calling policydsl.FromString with an expression that evaluates to a non-string, e.g. FromString("Org1.member") (a boolean/identifier, not a gate call), FromString("OutOf()"), an empty string, or an expression like '1 == 1' that expr evaluates to true rather than a gate string.

Common situations: Passing an empty or whitespace-only policy string from config; passing just an MSP principal without any And/Or/OutOf gate; copy-paste errors where the outer gate call was dropped; programmatic callers that build policy strings and concatenate incorrectly.

Related errors


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