wavetermdev/waveterm · error

Component function cannot b nil

Error message

Component function cannot b nil

What it means

validateCFunc performs reflection-based signature validation for functions passed to RegisterComponent. Its first check rejects a nil cfunc with "Component function cannot b nil" (note the typo 'b' is in the message). A nil component function cannot be invoked later when the frontend calls the component, so registration is refused immediately.

Source

Thrown at tsunami/engine/rootelem.go:238

	defer r.atomLock.Unlock()

	atom, ok := r.Atoms[name]
	if !ok {
		return fmt.Errorf("atom %q not found", name)
	}
	return atom.SetVal(val)
}

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 {

View on GitHub (pinned to a4447c1563)

Solutions

  1. Ensure the function is defined and assigned before calling RegisterComponent
  2. Check whatever produces the handler for nil returns (map lookups, config-driven wiring)
  3. Wrap registration in an error check and fail fast at startup rather than at render time

Example fix

// before
var handler func(map[string]any) any
root.RegisterComponent("counter", handler) // handler still nil
// after
handler := func(m map[string]any) any { return nil }
if err := root.RegisterComponent("counter", handler); err != nil { return err }
Defensive patterns

Strategy: validation

Validate before calling

if cfunc == nil {
    return fmt.Errorf("component %q: handler is nil", name)
}
if err := root.RegisterComponent(name, cfunc); err != nil { return err }

Type guard

func isNotNilFunc(v any) bool {
    if v == nil { return false }
    rv := reflect.ValueOf(v)
    return rv.Kind() == reflect.Func && !rv.IsNil()
}

Try / catch

if err := root.RegisterComponent("counter", cfunc); err != nil {
    return fmt.Errorf("register component failed: %w", err)
}

Prevention

When it happens

Trigger: RegisterComponent(name, nil); passing an untyped nil interface variable that was never assigned; a typed-nil value such as a nil func variable actually passes this check (it is not interface-nil) but is caught later.

Common situations: A factory/lookup returned nil (e.g. a map miss producing a nil handler) and the result was registered unchecked; wiring a component function from config where the key was absent.

Related errors


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