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

  1. Change the function's second return value to error (return nil error on success).
  2. If you need a bool/status, encode it in the single primary return or wrap in a struct, keeping error as the second return.
  3. Validate func signatures in a test that calls Funcs and recovers, or assert goodFunc(name, reflect.TypeOf(fn)) == nil.
  4. 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

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


AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09). Data as JSON: /api/errors/a8b3695cb5b62849. Report an issue: GitHub.