gohugoio/hugo · error

function %s has %d return values; should be 1 or 2

Error message

function %s has %d return values; should be 1 or 2

What it means

Returned/panicked by goodFunc when a registered function has zero or three-or-more return values. text/template FuncMap values must return exactly one value, or two values (the second being error). The %s is the func name; %d is the actual number of return values.

Source

Thrown at tpl/internal/go_templates/texttemplate/funcs.go:109

// 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
		}
	}
	return true

View on GitHub (pinned to 52c9bd7908)

Solutions

  1. Reduce the function to return exactly 1 value, or 2 values with the second being error.
  2. Wrap extra outputs into a single struct/value returned as the primary result.
  3. Add a unit test asserting each Funcs entry satisfies goodFunc before use.
  4. Document the (1) or (value, error) contract next to every registered func.

Example fix

// before
template.Funcs(template.FuncMap{
    "split3": func(s string) (string, string, string) { ... }, // error
})

// after
template.Funcs(template.FuncMap{
    "split3": func(s string) ([3]string, error) { return parts, nil },
})
Defensive patterns

Strategy: validation

Validate before calling

func checkReturnCounts(fm template.FuncMap) error {
    for name, fn := range fm {
        n := reflect.TypeOf(fn).NumOut()
        if n < 1 || n > 2 {
            return fmt.Errorf("%s returns %d values; need 1 or 2", name, n)
        }
    }
    return nil
}

Type guard

func hasValidArity(fn any) bool {
    t := reflect.TypeOf(fn)
    if t.Kind() != reflect.Func { return false }
    return t.NumOut() == 1 || t.NumOut() == 2
}

Try / catch

defer func() {
    if r := recover(); r != nil {
        log.Printf("func arity invalid: %v", r)
    }
}()
t.Funcs(fm)

Prevention

When it happens

Trigger: Registering a func via Funcs/addValueFuncs whose reflect type has NumOut() != 1 and != 2 (e.g. a func returning nothing, or returning three values). Also surfaces at execution via evalCall/the call builtin as an ExecError.

Common situations: A helper returning (a, b, c); a void action func returning nothing; passing a method value whose signature has multiple returns; accidental registration of a non-function-shaped value that resolves to an odd arity.

Related errors


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