{"record":{"id":"f131045a0b3a46f4","repo":"hyperledger/fabric","slug":"invalid-policy-string-s","errorCode":null,"errorMessage":"invalid policy string '%s'","messagePattern":"invalid policy string '(.+?)'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/policydsl/policyparser.go","lineNumber":290,"sourceCode":"\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tintermediateRes, err := expr.Run(intermediate, env)\n\tif err != nil {\n\t\t// attempt to produce a meaningful error\n\t\tif regexErr.MatchString(err.Error()) {\n\t\t\tsm := regexErr.FindStringSubmatch(err.Error())\n\t\t\tif len(sm) == 2 {\n\t\t\t\treturn nil, fmt.Errorf(\"unrecognized token '%s' in policy string\", sm[1])\n\t\t\t}\n\t\t}\n\n\t\treturn nil, err\n\t}\n\tresStr, ok := intermediateRes.(string)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"invalid policy string '%s'\", policy)\n\t}\n\n\t// we still need two passes. The first pass just adds an extra\n\t// argument ID to each of the outof calls. This is\n\t// required because govaluate has no means of giving context\n\t// to user-implemented functions other than via arguments.\n\t// We need this argument because we need a global place where\n\t// we put the identities that the policy requires\n\tenv = map[string]interface{}{\n\t\t\"outof\": firstPass,\n\t}\n\texp, err := expr.Compile(resStr, expr.Env(env))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tres, err := expr.Run(exp, env)\n\tif err != nil {\n\t\t// attempt to produce a meaningful error","sourceCodeStart":272,"sourceCodeEnd":308,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/common/policydsl/policyparser.go#L272-L308","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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')","Ensure the policy string is non-empty and contains no bare expressions that evaluate to booleans/numbers","Quote every principal argument ('Org1.member') so expr treats it as a string literal for the gate functions","Test the string with policydsl.FromString before embedding it in channel/chaincode policy configuration"],"exampleFix":"// before\nFromString(\"Org1.member\") // no gate -> result is not a string\n// after\nFromString(\"OutOf(1, 'Org1.member', 'Org1.admin')\")","handlingStrategy":"validation","validationCode":"func validatePolicyString(policy string) error {\n\ttrimmed := strings.TrimSpace(policy)\n\tif trimmed == \"\" {\n\t\treturn fmt.Errorf(\"policy string is empty\")\n\t}\n\tgateRe := regexp.MustCompile(`(?i)^(and|or|outof)\\s*\\(`)\n\tif !gateRe.MatchString(trimmed) {\n\t\treturn fmt.Errorf(\"policy %q must start with an And/Or/OutOf call\", policy)\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":"_, err := policydsl.FromString(policy)\nif err != nil && strings.Contains(err.Error(), \"invalid policy string\") {\n\treturn fmt.Errorf(\"policy %q is not a gate expression; wrap principals in And/Or/OutOf: %w\", policy, err)\n}","preventionTips":["Always root the policy in an And/Or/OutOf gate call - a bare principal is invalid","Reject empty/whitespace policy strings before calling FromString","Do not embed boolean/numeric expressions in policy strings","Add a config-load-time check that runs FromString on every policy"],"tags":["hyperledger-fabric","policies","dsl-parsing"],"backgroundTag":"policy-parse-error","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}