hyperledger/fabric · error
at least 3 arguments expected, got %d
Error message
at least 3 arguments expected, got %d
What it means
secondPass converts the intermediate outof string into an actual NOutOf SignaturePolicy. It needs at least 3 arguments: the injected ID context, the threshold t, and at least one principal/policy. Fewer than 3 means the intermediate structure was truncated or bypassed, so the pass aborts.
Source
Thrown at common/policydsl/policyparser.go:130
case float64:
toret.WriteString(strconv.Itoa(int(t)))
case int:
toret.WriteString(strconv.Itoa(t))
default:
return nil, fmt.Errorf("unexpected type %s", reflect.TypeOf(arg))
}
}
return toret.String() + ")", nil
}
// secondPass processes a list of arguments to build a "t-out-of-n" policy.
// It expects the first argument to be a context, the second an integer (threshold t),
// and the rest as either principals (strings) or pre-existing policies.
func secondPass(args ...any) (any, error) {
/* general sanity check, we expect at least 3 args */
if len(args) < 3 {
return nil, fmt.Errorf("at least 3 arguments expected, got %d", len(args))
}
/* get the first argument, we expect it to be the context */
var ctx *context
switch v := args[0].(type) {
case *context:
ctx = v
default:
return nil, fmt.Errorf("unrecognized type, expected the context, got %s", reflect.TypeOf(args[0]))
}
/* get the second argument, we expect an integer telling us
how many of the remaining we expect to have*/
var t int
switch arg := args[1].(type) {
case float64:
t = int(arg)
case int:View on GitHub (pinned to 2736b63f8f)
Solutions
- Ensure the original policy gate has at least one subject: 'OutOf(1, "Org1.member")'.
- If calling secondPass directly, pass (ctx *context, t int, principal-or-policy, ...).
- Validate generated policy strings contain at least one principal per gate before invoking FromString.
- Trace where the intermediate 'outof(...)' string is produced; fix the upstream missing-argument bug.
Example fix
// before
policydsl.FromString("And()") // gate with no subjects
// after
policydsl.FromString("And('Org1.member', 'Org2.member')") Defensive patterns
Strategy: validation
Validate before calling
if len(args) < 3 {
return fmt.Errorf("secondPass needs ctx, t, and at least one principal")
}
ctx, ok := args[0].(*context)
if !ok {
return fmt.Errorf("first arg must be *context")
} Type guard
func isContextCall(args []any) bool {
if len(args) < 3 { return false }
_, ok := args[0].(*context)
return ok
} Try / catch
policy, err := policydsl.FromString(spec)
if err != nil && strings.Contains(err.Error(), "at least 3 arguments expected") {
return nil, fmt.Errorf("policy %q has an empty gate: %w", spec, err)
} Prevention
- Never call secondPass directly; go through FromString which injects the ID context.
- Ensure every gate in the policy string has at least one principal.
- Validate generated policies with a pre-parse sanity check on gate arity.
When it happens
Trigger: An intermediate string like 'outof(ID)' or 'outof(ID, 1)' reaching the second pass — e.g. the original policy had a gate with no subjects (related to error 350 but caught later), or secondPass invoked directly with too few args.
Common situations: Empty org lists in generated policy code producing subject-less gates; custom pipelines that call secondPass without the injected ID argument; policy strings mangled by templating that dropped trailing arguments.
Related errors
- expected at least two arguments to NOutOf. Given %d
- unexpected type %s
- unrecognized type, expected the context, got %s
- unrecognized type, expected a number, got %s
- invalid t-out-of-n predicate, t %d, n %d
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/4e5e0c1d6cb93111.
Report an issue: GitHub.