{"record":{"id":"0ac5c3a167b1ca0c","repo":"wavetermdev/waveterm","slug":"map-argument-must-be-map-string-any-0ac5c3","errorCode":null,"errorMessage":"Map argument must be map[string]any","messagePattern":"Map argument must be map\\[string\\]any","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tsunami/engine/rootelem.go","lineNumber":259,"sourceCode":"\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 {\n\t\tif arg1Type.Key().Kind() != reflect.String ||\n\t\t\t!(arg1Type.Elem().Kind() == reflect.Interface && arg1Type.Elem().NumMethod() == 0) {\n\t\t\treturn fmt.Errorf(\"Map argument must be map[string]any\")\n\t\t}\n\t} else if arg1Type.Kind() != reflect.Struct &&\n\t\t!(arg1Type.Kind() == reflect.Interface && arg1Type.NumMethod() == 0) {\n\t\treturn fmt.Errorf(\"Component function argument must be map[string]any, struct, or any\")\n\t}\n\treturn nil\n}\n\nfunc (r *RootElem) RegisterComponent(name string, cfunc any) error {\n\tif err := validateCFunc(cfunc); err != nil {\n\t\treturn err\n\t}\n\tr.CFuncs[name] = cfunc\n\treturn nil\n}\n\nfunc callVDomFn(fnVal any, data vdom.VDomEvent) {\n\tif fnVal == nil {","sourceCodeStart":241,"sourceCodeEnd":277,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/tsunami/engine/rootelem.go#L241-L277","documentation":"If the component function's single argument is a map, validateCFunc requires it to be exactly map[string]any (string keys, empty-interface values, no methods on the element type). Any other map shape — e.g. map[string]string or map[string]int — is rejected with \"Map argument must be map[string]any\". Pointer-to-map arguments are dereferenced before this check.","triggerScenarios":"RegisterComponent with func(map[string]string) any; func(map[string]interface{}) on a named map type whose element is not plain any; a custom key type like map[int]any.","commonSituations":"Declaring typed maps to get compile-time safety, not realizing the reflection contract demands map[string]any; passing an aliased map type whose element kind is not empty interface.","solutions":["Change the parameter type to map[string]any and convert inside the function body","If you want type safety, use a struct parameter instead of a typed map","Remove named/aliased map types in favor of the literal map[string]any"],"exampleFix":"// before\nroot.RegisterComponent(\"table\", func(cols map[string]string) any { ... })\n// after\nroot.RegisterComponent(\"table\", func(props map[string]any) any {\n    cols := toStringMap(props[\"cols\"]) // convert inside\n    return renderTable(cols)\n})","handlingStrategy":"validation","validationCode":"t := reflect.TypeOf(cfunc)\nif t != nil && t.Kind() == reflect.Func && t.NumIn() == 1 {\n    at := t.In(0)\n    if at.Kind() == reflect.Ptr { at = at.Elem() }\n    if at.Kind() == reflect.Map && (at.Key().Kind() != reflect.String || at.Elem().Kind() != reflect.Interface) {\n        return fmt.Errorf(\"map param must be map[string]any, got %v\", t.In(0))\n    }\n}","typeGuard":"func takesMapStringAny(v any) bool {\n    t := reflect.TypeOf(v)\n    if t == nil || t.Kind() != reflect.Func || t.NumIn() != 1 { return false }\n    at := t.In(0)\n    if at.Kind() == reflect.Ptr { at = at.Elem() }\n    return at.Kind() == reflect.Map && at.Key().Kind() == reflect.String &&\n        at.Elem().Kind() == reflect.Interface && at.Elem().NumMethod() == 0\n}","tryCatchPattern":"if err := root.RegisterComponent(name, fn); err != nil {\n    return fmt.Errorf(\"map param must be map[string]any: %w\", err)\n}","preventionTips":["Use the literal type map[string]any for props maps, never typed maps","Prefer struct parameters when you want compile-time key/value typing","Convert typed maps inside the function body, not in its signature"],"tags":["go","reflection","map-types","signature"],"backgroundTag":"callback-signature-mismatch","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}