{"id":"0ca229cf4857dbbe","repo":"gohugoio/hugo","slug":"with-2-arguments-the-first-must-be-a-slice-of-rep","errorCode":null,"errorMessage":"with 2 arguments, the first must be a slice of replacement pairs, got %T","messagePattern":"with 2 arguments, the first must be a slice of replacement pairs, got %T","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tpl/strings/strings.go","lineNumber":277,"sourceCode":"\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}\n\tif p == nil {\n\t\t// inline form: ReplacePairs \"a\" \"b\" \"s\"\n\t\tp = make([]string, len(args)-1)\n\t\tfor i, v := range args[:len(args)-1] {\n\t\t\ts, err := cast.ToStringE(v)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\tp[i] = s\n\t\t}\n\t}\n","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/gohugoio/hugo/blob/8a468df065a75c1c7cf9f6850f32148746590ea5/tpl/strings/strings.go#L259-L295","documentation":"When `strings.ReplacePairs` receives exactly 2 arguments, it interprets them as `(pairs-slice, source-string)`. It checks the first argument with `hreflect.IsSlice`; if it isn't a slice, the call is ambiguous — two bare strings can't form both a complete pair and a source — so Hugo errors, reporting the actual Go type it received via `%T`.","triggerScenarios":"`{{ strings.ReplacePairs \"old\" \"new\" }}` — two plain strings with no source; `{{ strings.ReplacePairs $pairs $src }}` where $pairs is actually a string, map/dict, or nil rather than a slice; passing a dict (`dict \"a\" \"b\"`) instead of `slice \"a\" \"b\"`.","commonSituations":"Thinking the function works like `replace` (old, new) and forgetting the source string; building pairs with `dict` instead of `slice`; a front-matter param expected to be a YAML list but written as a string or map.","solutions":["Use the slice form with a source: `{{ strings.ReplacePairs (slice \"old\" \"new\") $src }}`.","For a single inline pair, supply three arguments: `{{ strings.ReplacePairs \"old\" \"new\" $src }}`.","If pairs come from front matter, define them as a YAML list (`pairs: [\"a\",\"b\"]`), not a map — the error's %T output tells you what type actually arrived."],"exampleFix":"<!-- before -->\n{{ strings.ReplacePairs \"old\" \"new\" }}\n<!-- after -->\n{{ strings.ReplacePairs \"old\" \"new\" .Content }}","handlingStrategy":"type-guard","validationCode":"{{ $pairs := slice \"a\" \"b\" \"c\" \"d\" }}{{ if reflect.IsSlice $pairs }}{{ strings.Replacer $s $pairs }}{{ end }}","typeGuard":"func isPairSlice(v any) bool {\n    rv := reflect.ValueOf(v)\n    return rv.Kind() == reflect.Slice && rv.Len()%2 == 0\n}","tryCatchPattern":"out, err := ns.Replacer(s, pairs)\nif err != nil {\n    // pairs wasn't a slice — check %T in the message and fix the data shape\n}","preventionTips":["In the 2-argument form, pass a flat slice (`slice \"old\" \"new\" ...`), not a map or string","Use `reflect.IsSlice` in templates to guard values loaded from params/data files","Prefer the variadic form with literal arguments when pairs are static"],"tags":["hugo","go-templates","strings","arguments","type-conversion"],"analyzedSha":"8a468df065a75c1c7cf9f6850f32148746590ea5","analyzedAt":"2026-07-31T21:23:07.045Z","schemaVersion":2}