{"id":"2ec9fb27aba1ebca","repo":"gohugoio/hugo","slug":"operator-argument-must-be-string-type","errorCode":null,"errorMessage":"operator argument must be string type","messagePattern":"operator argument must be string type","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tpl/collections/where.go","lineNumber":396,"sourceCode":"\t\tkv := reflect.ValueOf(elemName)\n\t\tif kv.Type().AssignableTo(obj.Type().Key()) {\n\t\t\treturn obj.MapIndex(kv), nil\n\t\t}\n\t\treturn zero, fmt.Errorf(\"%s isn't a key of map type %s\", elemName, typ)\n\t}\n\treturn zero, fmt.Errorf(\"%s is neither a struct field, a method nor a map element of type %s\", elemName, typ)\n}\n\n// parseWhereArgs parses the end arguments to the where function.  Return a\n// match value and an operator, if one is defined.\nfunc parseWhereArgs(args ...any) (mv reflect.Value, op string, err error) {\n\tswitch len(args) {\n\tcase 1:\n\t\tmv = reflect.ValueOf(args[0])\n\tcase 2:\n\t\tvar ok bool\n\t\tif op, ok = args[0].(string); !ok {\n\t\t\terr = errors.New(\"operator argument must be string type\")\n\t\t\treturn\n\t\t}\n\t\top = strings.TrimSpace(strings.ToLower(op))\n\t\tmv = reflect.ValueOf(args[1])\n\tdefault:\n\t\terr = errors.New(\"can't evaluate the array by no match argument or more than or equal to two arguments\")\n\t}\n\treturn\n}\n\n// elemResolver resolves a sub-element from a reflect.Value.\n// Built once before the loop to avoid repeated type checks and reflect.ValueOf allocations.\ntype elemResolver func(reflect.Value) (reflect.Value, error)\n\n// newElemResolver returns a resolver optimized for the given element type and path.\n// Returns nil if optimization isn't possible, in which case the caller should\n// fall back to evaluateSubElem.\nfunc (ns *Namespace) newElemResolver(ctxv reflect.Value, elemType reflect.Type, path []string) elemResolver {","sourceCodeStart":378,"sourceCodeEnd":414,"githubUrl":"https://github.com/gohugoio/hugo/blob/8a468df065a75c1c7cf9f6850f32148746590ea5/tpl/collections/where.go#L378-L414","documentation":"parseWhereArgs received two trailing arguments, meaning the third positional argument to `where` must be an operator string (\"==\", \"!=\", \"in\", \"like\", ...), but args[0] failed the string type assertion. Hugo requires the operator to be a literal string to dispatch the comparison.","triggerScenarios":"`where COLLECTION KEY OPERATOR MATCH` where OPERATOR is not a string — e.g. `where .Pages \"Params.weight\" 1 10` (a number in the operator slot), or passing a variable that holds a non-string.","commonSituations":"Forgetting to quote the operator (`>=` unquoted parses oddly), swapping operator and match argument order, generating the call via `partial` params where the operator became a number or bool.","solutions":["Quote the operator: `where .Pages \"Params.weight\" \">=\" 10`.","Check argument order: key, operator, match value — operator third.","If templating the operator, ensure the variable is a string (`printf \"%s\" $op`)."],"exampleFix":"<!-- before -->\n{{ range where .Site.RegularPages \"Params.weight\" 5 10 }}\n<!-- after -->\n{{ range where .Site.RegularPages \"Params.weight\" \">=\" 10 }}","handlingStrategy":"type-guard","validationCode":"{{ $op := \"ge\" }}\n{{ if not (eq (printf \"%T\" $op) \"string\") }}{{ errorf \"where operator must be a string\" }}{{ end }}","typeGuard":"{{/* only pass literal operator strings: \"==\", \"!=\", \">=\", \"in\", \"intersect\", ... */}}","tryCatchPattern":null,"preventionTips":["Pass the operator as a quoted string literal (\"ge\", \"in\"), not a variable of another type","When the operator comes from config/params, cast with string or validate its type first","Use the 2-arg form (implicit ==) when no operator is needed"],"tags":["hugo","templates","where","arguments"],"analyzedSha":"8a468df065a75c1c7cf9f6850f32148746590ea5","analyzedAt":"2026-07-31T21:23:07.045Z","schemaVersion":2}