{"record":{"id":"a8b3695cb5b62849","repo":"gohugoio/hugo","slug":"invalid-function-signature-for-s-second-return-v","errorCode":null,"errorMessage":"invalid function signature for %s: second return value should be error; is %s","messagePattern":"invalid function signature for (.+?): second return value should be error; is (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tpl/internal/go_templates/texttemplate/funcs.go","lineNumber":107,"sourceCode":"\n// addFuncs adds to values the functions in funcs. It does no checking of the input -\n// call addValueFuncs first.\nfunc addFuncs(out, in FuncMap) {\n\tfor name, fn := range in {\n\t\tout[name] = fn\n\t}\n}\n\n// goodFunc reports whether the function or method has the right result signature.\nfunc goodFunc(name string, typ reflect.Type) error {\n\t// We allow functions with 1 result or 2 results where the second is an error.\n\tswitch numOut := typ.NumOut(); {\n\tcase numOut == 1:\n\t\treturn nil\n\tcase numOut == 2 && typ.Out(1) == errorType:\n\t\treturn nil\n\tcase numOut == 2:\n\t\treturn fmt.Errorf(\"invalid function signature for %s: second return value should be error; is %s\", name, typ.Out(1))\n\tdefault:\n\t\treturn fmt.Errorf(\"function %s has %d return values; should be 1 or 2\", name, typ.NumOut())\n\t}\n}\n\n// goodName reports whether the function name is a valid identifier.\nfunc goodName(name string) bool {\n\tif name == \"\" {\n\t\treturn false\n\t}\n\tfor i, r := range name {\n\t\tswitch {\n\t\tcase r == '_':\n\t\tcase i == 0 && !unicode.IsLetter(r):\n\t\t\treturn false\n\t\tcase !unicode.IsLetter(r) && !unicode.IsDigit(r):\n\t\t\treturn false\n\t\t}","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/gohugoio/hugo/blob/52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2/tpl/internal/go_templates/texttemplate/funcs.go#L89-L125","documentation":"Returned/panicked by goodFunc when a registered function has exactly two return values but the second is not of type error. text/template requires FuncMap values to return either one value, or two values where the second is error (used to propagate execution errors). The %s is the func name; the second %s is the actual second return type.","triggerScenarios":"Registering a func via Funcs(funcMap) or addValueFuncs whose Go signature returns (T, U) where U is not error. goodFunc is also invoked at execution time (evalCall / the call builtin) where it surfaces as an ExecError rather than a panic.","commonSituations":"Defining a helper that returns (value, ok bool) instead of (value, error); a func returning (result, status) custom types; refactoring a func's return signature and forgetting templates use it; copy-paste from non-template code.","solutions":["Change the function's second return value to error (return nil error on success).","If you need a bool/status, encode it in the single primary return or wrap in a struct, keeping error as the second return.","Validate func signatures in a test that calls Funcs and recovers, or assert goodFunc(name, reflect.TypeOf(fn)) == nil.","Keep a single canonical list of template funcs with reviewed signatures."],"exampleFix":"// before\ntemplate.Funcs(template.FuncMap{\n    \"lookup\": func(k string) (string, bool) { return v, ok }, // error\n})\n\n// after\ntemplate.Funcs(template.FuncMap{\n    \"lookup\": func(k string) (string, error) { return v, nil },\n})","handlingStrategy":"validation","validationCode":"func validateFuncs(fm template.FuncMap) error {\n    for name, fn := range fm {\n        t := reflect.TypeOf(fn)\n        if t.Kind() != reflect.Func { return fmt.Errorf(\"%s not a func\", name) }\n        switch n := t.NumOut(); {\n        case n == 1:\n        case n == 2 && t.Out(1) == reflect.TypeFor[error]():\n        default:\n            return fmt.Errorf(\"%s: bad signature (out=%d)\", name, n)\n        }\n    }\n    return nil\n}","typeGuard":"func isTemplateFunc(fn any) bool {\n    t := reflect.TypeOf(fn)\n    if t.Kind() != reflect.Func { return false }\n    switch t.NumOut() {\n    case 1: return true\n    case 2: return t.Out(1) == reflect.TypeFor[error]()\n    }\n    return false\n}","tryCatchPattern":"defer func() {\n    if r := recover(); r != nil {\n        return fmt.Errorf(\"func registration failed: %v\", r)\n    }\n}()\nt = t.Funcs(fm)","preventionTips":["Adopt the (value) or (value, error) contract for every template func.","Write a unit test asserting each Funcs entry passes goodFunc.","Review signature changes to funcs used by templates."],"tags":["go","text-template","funcs","reflect","signature"],"backgroundTag":null,"analyzedSha":"52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2","analyzedAt":"2026-08-09T21:49:36.660Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}