wavetermdev/waveterm · error

Component function must return exactly 1 value

Error message

Component function must return exactly 1 value

What it means

The engine treats the single return value of a component function as the rendered component output, so validateCFunc requires rtype.NumOut() == 1. Functions returning zero values or multiple values (e.g. (any, error)) are rejected with "Component function must return exactly 1 value".

Source

Thrown at tsunami/engine/rootelem.go:249

	defer r.atomLock.Unlock()

	delete(r.Atoms, name)
}

func validateCFunc(cfunc any) error {
	if cfunc == nil {
		return fmt.Errorf("Component function cannot b nil")
	}
	rval := reflect.ValueOf(cfunc)
	if rval.Kind() != reflect.Func {
		return fmt.Errorf("Component function must be a function")
	}
	rtype := rval.Type()
	if rtype.NumIn() != 1 {
		return fmt.Errorf("Component function must take exactly 1 argument")
	}
	if rtype.NumOut() != 1 {
		return fmt.Errorf("Component function must return exactly 1 value")
	}
	// first argument can be a map[string]any, or a struct, or ptr to struct (we'll reflect the value into it)
	arg1Type := rtype.In(0)
	if arg1Type.Kind() == reflect.Ptr {
		arg1Type = arg1Type.Elem()
	}
	if arg1Type.Kind() == reflect.Map {
		if arg1Type.Key().Kind() != reflect.String ||
			!(arg1Type.Elem().Kind() == reflect.Interface && arg1Type.Elem().NumMethod() == 0) {
			return fmt.Errorf("Map argument must be map[string]any")
		}
	} else if arg1Type.Kind() != reflect.Struct &&
		!(arg1Type.Kind() == reflect.Interface && arg1Type.NumMethod() == 0) {
		return fmt.Errorf("Component function argument must be map[string]any, struct, or any")
	}
	return nil
}

View on GitHub (pinned to a4447c1563)

Solutions

  1. Make the function return exactly one value (the component output)
  2. If errors are possible, handle them inside the function and return an error component/value instead
  3. Wrap multi-return helpers in a single-return closure

Example fix

// before
root.RegisterComponent("stats", func(p map[string]any) (any, error) { ... })
// after
root.RegisterComponent("stats", func(p map[string]any) any {
    v, err := computeStats(p)
    if err != nil { return errorView(err) }
    return v
})
Defensive patterns

Strategy: validation

Validate before calling

t := reflect.TypeOf(cfunc)
if t != nil && t.Kind() == reflect.Func && t.NumOut() != 1 {
    return fmt.Errorf("need exactly 1 return value, got %d", t.NumOut())
}

Type guard

func isSingleReturnFunc(v any) bool {
    t := reflect.TypeOf(v)
    return t != nil && t.Kind() == reflect.Func && t.NumIn() == 1 && t.NumOut() == 1
}

Try / catch

if err := root.RegisterComponent(name, fn); err != nil {
    return fmt.Errorf("component %s must return exactly 1 value: %w", name, err)
}

Prevention

When it happens

Trigger: RegisterComponent with func(map[string]any) (any, error); registering a function that returns nothing (func(map[string]any) {}); registering a helper whose idiomatic Go signature is (T, error).

Common situations: Returning (result, error) out of Go habit; registering an existing function with a multi-value signature instead of wrapping it; registering a void procedure instead of a render function.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/074b504e35acd13a. Report an issue: GitHub.