{"id":"0eeb5a114e22f537","repo":"gohugoio/hugo","slug":"requires-at-least-2-arguments","errorCode":null,"errorMessage":"requires at least 2 arguments","messagePattern":"requires at least 2 arguments","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tpl/strings/strings.go","lineNumber":265,"sourceCode":"\n\tif len(limit) == 0 {\n\t\treturn strings.ReplaceAll(ss, so, sn), nil\n\t}\n\n\tlim, err := cast.ToIntE(limit[0])\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn strings.Replace(ss, so, sn, lim), nil\n}\n\n// ReplacePairs returns a copy of a string with multiple replacements performed\n// in a single pass. The last argument is the source string. Preceding arguments\n// are old/new string pairs, either as a slice or as individual arguments.\nfunc (ns *Namespace) ReplacePairs(args ...any) (string, error) {\n\tif len(args) < 2 {\n\t\treturn \"\", fmt.Errorf(\"requires at least 2 arguments\")\n\t}\n\n\tss, err := cast.ToStringE(args[len(args)-1])\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tvar p []string\n\tif len(args) == 2 {\n\t\t// slice form: ReplacePairs (slice \"a\" \"b\") \"s\"\n\t\tif !hreflect.IsSlice(args[0]) {\n\t\t\treturn \"\", fmt.Errorf(\"with 2 arguments, the first must be a slice of replacement pairs, got %T\", args[0])\n\t\t}\n\t\tp, err = cast.ToStringSliceE(args[0])\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}","sourceCodeStart":247,"sourceCodeEnd":283,"githubUrl":"https://github.com/gohugoio/hugo/blob/8a468df065a75c1c7cf9f6850f32148746590ea5/tpl/strings/strings.go#L247-L283","documentation":"Hugo's `strings.ReplacePairs` is variadic: the last argument is the source string and everything before it is old/new replacement pairs (as a slice or inline). With fewer than 2 arguments there is no way to have both a source string and at least one pair specification, so Hugo rejects the call up front.","triggerScenarios":"Calling `{{ strings.ReplacePairs \"source\" }}` with only the source string, or `{{ strings.ReplacePairs }}` with nothing — e.g. building the argument list dynamically and ending up with an empty/singleton set, or piping a lone string: `{{ \"s\" | strings.ReplacePairs }}`.","commonSituations":"Misunderstanding the signature (expecting a `replace`-like `old new src` order but supplying only the source); a `with`/`range` construct that drops the pairs variable so only the piped source remains; slice of pairs evaluating to nothing and being spread incorrectly.","solutions":["Provide both a pairs argument and the source: slice form `{{ strings.ReplacePairs (slice \"a\" \"b\") $src }}` or inline form `{{ strings.ReplacePairs \"a\" \"b\" $src }}`.","Remember the source string comes LAST — this differs from `replace`.","If pairs are built dynamically, verify the slice is non-empty before calling."],"exampleFix":"<!-- before -->\n{{ strings.ReplacePairs $src }}\n<!-- after -->\n{{ strings.ReplacePairs (slice \"old\" \"new\") $src }}","handlingStrategy":"validation","validationCode":"{{ if ge (len $pairs) 2 }}{{ strings.Replacer $s $pairs }}{{ end }}","typeGuard":null,"tryCatchPattern":"if len(args) < 2 {\n    return \"\", errors.New(\"need at least old/new pair\")\n}\nout, err := ns.Replacer(args...)","preventionTips":["Always call the replacer with at least one old/new pair","When pairs come from site data, assert `len` before the call","Fail the build early on empty replacement config rather than skipping silently"],"tags":["hugo","go-templates","strings","arguments"],"analyzedSha":"8a468df065a75c1c7cf9f6850f32148746590ea5","analyzedAt":"2026-07-31T21:23:07.045Z","schemaVersion":2}