gohugoio/hugo · error
invalid function signature for %s: second return value shoul
Error message
invalid function signature for %s: second return value should be error; is %s
What it means
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.
Source
Thrown at tpl/internal/go_templates/texttemplate/funcs.go:107
// addFuncs adds to values the functions in funcs. It does no checking of the input -
// call addValueFuncs first.
func addFuncs(out, in FuncMap) {
for name, fn := range in {
out[name] = fn
}
}
// goodFunc reports whether the function or method has the right result signature.
func goodFunc(name string, typ reflect.Type) error {
// We allow functions with 1 result or 2 results where the second is an error.
switch numOut := typ.NumOut(); {
case numOut == 1:
return nil
case numOut == 2 && typ.Out(1) == errorType:
return nil
case numOut == 2:
return fmt.Errorf("invalid function signature for %s: second return value should be error; is %s", name, typ.Out(1))
default:
return fmt.Errorf("function %s has %d return values; should be 1 or 2", name, typ.NumOut())
}
}
// goodName reports whether the function name is a valid identifier.
func goodName(name string) bool {
if name == "" {
return false
}
for i, r := range name {
switch {
case r == '_':
case i == 0 && !unicode.IsLetter(r):
return false
case !unicode.IsLetter(r) && !unicode.IsDigit(r):
return false
}View on GitHub (pinned to 52c9bd7908)
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.
Example fix
// before
template.Funcs(template.FuncMap{
"lookup": func(k string) (string, bool) { return v, ok }, // error
})
// after
template.Funcs(template.FuncMap{
"lookup": func(k string) (string, error) { return v, nil },
}) Defensive patterns
Strategy: validation
Validate before calling
func validateFuncs(fm template.FuncMap) error {
for name, fn := range fm {
t := reflect.TypeOf(fn)
if t.Kind() != reflect.Func { return fmt.Errorf("%s not a func", name) }
switch n := t.NumOut(); {
case n == 1:
case n == 2 && t.Out(1) == reflect.TypeFor[error]():
default:
return fmt.Errorf("%s: bad signature (out=%d)", name, n)
}
}
return nil
} Type guard
func isTemplateFunc(fn any) bool {
t := reflect.TypeOf(fn)
if t.Kind() != reflect.Func { return false }
switch t.NumOut() {
case 1: return true
case 2: return t.Out(1) == reflect.TypeFor[error]()
}
return false
} Try / catch
defer func() {
if r := recover(); r != nil {
return fmt.Errorf("func registration failed: %v", r)
}
}()
t = t.Funcs(fm) Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- function %s has %d return values; should be 1 or 2
- value is nil; should be of type %s
- value has type %s; should be %s
- cannot index slice/array with nil
- cannot index slice/array with type %s
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/a8b3695cb5b62849.
Report an issue: GitHub.