{"record":{"id":"2b8cb2ea6b63a4d8","repo":"wavetermdev/waveterm","slug":"component-function-cannot-b-nil-2b8cb2","errorCode":null,"errorMessage":"Component function cannot b nil","messagePattern":"Component function cannot b nil","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tsunami/engine/rootelem.go","lineNumber":238,"sourceCode":"\tdefer r.atomLock.Unlock()\n\n\tatom, ok := r.Atoms[name]\n\tif !ok {\n\t\treturn fmt.Errorf(\"atom %q not found\", name)\n\t}\n\treturn atom.SetVal(val)\n}\n\nfunc (r *RootElem) RemoveAtom(name string) {\n\tr.atomLock.Lock()\n\tdefer r.atomLock.Unlock()\n\n\tdelete(r.Atoms, name)\n}\n\nfunc validateCFunc(cfunc any) error {\n\tif cfunc == nil {\n\t\treturn fmt.Errorf(\"Component function cannot b nil\")\n\t}\n\trval := reflect.ValueOf(cfunc)\n\tif rval.Kind() != reflect.Func {\n\t\treturn fmt.Errorf(\"Component function must be a function\")\n\t}\n\trtype := rval.Type()\n\tif rtype.NumIn() != 1 {\n\t\treturn fmt.Errorf(\"Component function must take exactly 1 argument\")\n\t}\n\tif rtype.NumOut() != 1 {\n\t\treturn fmt.Errorf(\"Component function must return exactly 1 value\")\n\t}\n\t// first argument can be a map[string]any, or a struct, or ptr to struct (we'll reflect the value into it)\n\targ1Type := rtype.In(0)\n\tif arg1Type.Kind() == reflect.Ptr {\n\t\targ1Type = arg1Type.Elem()\n\t}\n\tif arg1Type.Kind() == reflect.Map {","sourceCodeStart":220,"sourceCodeEnd":256,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/tsunami/engine/rootelem.go#L220-L256","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure the function is defined and assigned before calling RegisterComponent","Check whatever produces the handler for nil returns (map lookups, config-driven wiring)","Wrap registration in an error check and fail fast at startup rather than at render time"],"exampleFix":"// before\nvar handler func(map[string]any) any\nroot.RegisterComponent(\"counter\", handler) // handler still nil\n// after\nhandler := func(m map[string]any) any { return nil }\nif err := root.RegisterComponent(\"counter\", handler); err != nil { return err }","handlingStrategy":"validation","validationCode":"if cfunc == nil {\n    return fmt.Errorf(\"component %q: handler is nil\", name)\n}\nif err := root.RegisterComponent(name, cfunc); err != nil { return err }","typeGuard":"func isNotNilFunc(v any) bool {\n    if v == nil { return false }\n    rv := reflect.ValueOf(v)\n    return rv.Kind() == reflect.Func && !rv.IsNil()\n}","tryCatchPattern":"if err := root.RegisterComponent(\"counter\", cfunc); err != nil {\n    return fmt.Errorf(\"register component failed: %w\", err)\n}","preventionTips":["Fail fast at startup: register all components in init and check every error","When handlers come from lookups, check for nil before registering","Avoid untyped nil interface variables when wiring handlers from config"],"tags":["go","nil-value","registration","components"],"backgroundTag":"nil-callback-registration","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}