{"record":{"id":"6eec6514ab4fe5a4","repo":"hyperledger/fabric","slug":"unexpected-type-s","errorCode":null,"errorMessage":"unexpected type %s","messagePattern":"unexpected type (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/policydsl/policyparser.go","lineNumber":65,"sourceCode":"// This will be evaluated by second/third passes to convert to a proto policy\nfunc outof(args ...any) (any, error) {\n\tvar toret strings.Builder\n\ttoret.WriteString(\"outof(\")\n\n\tif len(args) < 2 {\n\t\treturn nil, fmt.Errorf(\"expected at least two arguments to NOutOf. Given %d\", len(args))\n\t}\n\n\targ0 := args[0]\n\t// govaluate treats all numbers as float64 only. But and/or may pass int/string. Allowing int/string for flexibility of caller\n\tif n, ok := arg0.(float64); ok {\n\t\ttoret.WriteString(strconv.Itoa(int(n)))\n\t} else if n, ok := arg0.(int); ok {\n\t\ttoret.WriteString(strconv.Itoa(n))\n\t} else if n, ok := arg0.(string); ok {\n\t\ttoret.WriteString(n)\n\t} else {\n\t\treturn nil, fmt.Errorf(\"unexpected type %s\", reflect.TypeOf(arg0))\n\t}\n\n\tfor _, arg := range args[1:] {\n\t\ttoret.WriteString(\", \")\n\n\t\tswitch t := arg.(type) {\n\t\tcase string:\n\t\t\tif regex.MatchString(t) {\n\t\t\t\ttoret.WriteString(\"'\" + t + \"'\")\n\t\t\t} else {\n\t\t\t\ttoret.WriteString(t)\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unexpected type %s\", reflect.TypeOf(arg))\n\t\t}\n\t}\n\n\treturn toret.String() + \")\", nil","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/common/policydsl/policyparser.go#L47-L83","documentation":"In outof(), the first argument is the threshold t of a t-out-of-n policy and must be a float64, int, or string. Any other type (bool, struct, nil, etc.) cannot be rendered into the intermediate policy string, so the library fails with 'unexpected type <T>'.","triggerScenarios":"Calling policydsl.OutOf(true, ...), OutOf(nil, ...), or OutOf(someStruct, ...) directly, or building gates programmatically where the threshold slot receives a non-numeric, non-string value.","commonSituations":"Passing an unsigned/int64/int32 threshold from config code without converting to int; accidentally swapping arguments so a principal struct lands in the threshold slot; using a *cb.SignaturePolicy where a number is expected.","solutions":["Pass the threshold as int, float64, or a numeric string: OutOf(2, ...) or OutOf(\"2\", ...).","Convert other numeric types before the call, e.g. OutOf(int(n64), principals...).","Check argument order — the threshold must be args[0], principals after it.","If generated by code, add a type assertion/switch on the threshold value before invoking outof."],"exampleFix":"// before\npolicydsl.OutOf(uint(2), \"Org1.member\") // uint is unsupported\n// after\npolicydsl.OutOf(int(2), \"Org1.member\")","handlingStrategy":"type-guard","validationCode":"switch t.(type) {\ncase int, float64, string:\n\t// ok\ndefault:\n\treturn fmt.Errorf(\"threshold must be int/float64/string, got %T\", t)\n}","typeGuard":"func isValidThreshold(v any) bool {\n\tswitch v.(type) {\n\tcase int, float64, string:\n\t\treturn true\n\t}\n\treturn false\n}","tryCatchPattern":"_, err := policydsl.OutOf(threshold, principals...)\nif err != nil && strings.Contains(err.Error(), \"unexpected type\") {\n\treturn nil, fmt.Errorf(\"threshold %v (%T) is not numeric/string\", threshold, threshold)\n}","preventionTips":["Pass thresholds as int (or float64/string); convert int64/uint/float32 first.","Keep the threshold as the first argument, principals after.","Never pass protos, bools, or nil in the threshold slot.","Add a typed Gate builder struct that only accepts int thresholds."],"tags":["policydsl","hyperledger-fabric","type-error","threshold"],"backgroundTag":"unexpected-argument-type","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}