wavetermdev/waveterm · error

Component function must take exactly 1 argument

Error message

Component function must take exactly 1 argument

What it means

Component functions are invoked by the engine with exactly one argument (the props/data from the frontend), so validateCFunc requires rtype.NumIn() == 1. Functions with zero arguments or two or more arguments are rejected with "Component function must take exactly 1 argument".

Source

Thrown at tsunami/engine/rootelem.go:246

func (r *RootElem) RemoveAtom(name string) {
	r.atomLock.Lock()
	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")
	}

View on GitHub (pinned to a4447c1563)

Solutions

  1. Change the function to accept exactly one argument: the props as map[string]any, a struct, *struct, or any
  2. If extra parameters are needed, close over them in a closure that takes a single argument
  3. Split multi-argument logic into a helper and register a single-arg wrapper

Example fix

// before
root.RegisterComponent("clock", func(tz string, fmtStr string) any { ... })
// after
tz, fmtStr := "UTC", time.Kitchen
root.RegisterComponent("clock", func(props map[string]any) any {
    return renderClock(tz, fmtStr) // capture extras via closure
})
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

func isSingleArgFunc(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 signature invalid: %w", name, err)
}

Prevention

When it happens

Trigger: RegisterComponent with func() any (0 args); RegisterComponent with func(ctx context.Context, props map[string]any) any (2 args); binding a standard handler that takes (w, r) style pairs.

Common situations: Reusing an ordinary helper function as a component function; adapting a generic event handler signature; adding a context parameter during refactoring without matching the library's single-argument convention.

Related errors


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