{"record":{"id":"7e750860c48a3fed","repo":"JeffreySu/WeiXinMPSDK","slug":"key-commonapi-menu","errorCode":null,"errorMessage":"单击按钮的key不能为空！","messagePattern":"单击按钮的key不能为空！","errorType":"validation","errorClass":"WeixinMenuException","httpStatus":null,"severity":"error","filePath":"src/Senparc.Weixin.Work/Senparc.Weixin.Work/CommonAPIs/CommonApi.Menu.cs","lineNumber":244,"sourceCode":"            try\n            {\n                //重新整理按钮信息\n                ButtonGroup bg = new ButtonGroup();\n                foreach (var rootButton in resultFull.button)\n                {\n                    if (rootButton.name == null)\n                    {\n                        continue;//没有设置一级菜单\n                    }\n                    var availableSubButton = rootButton.sub_button.Count(z => !string.IsNullOrEmpty(z.name));//可用二级菜单按钮数量\n                    if (availableSubButton == 0)\n                    {\n                        //底部单击按钮\n                        if (rootButton.type == null ||\n                            (rootButton.type.Equals(\"CLICK\", StringComparison.OrdinalIgnoreCase)\n                            && string.IsNullOrEmpty(rootButton.key)))\n                        {\n                            throw new WeixinMenuException(\"单击按钮的key不能为空！\");\n                        }\n\n                        if (rootButton.type.Equals(\"CLICK\", StringComparison.OrdinalIgnoreCase))\n                        {\n                            //点击\n                            bg.button.Add(new SingleClickButton()\n                            {\n                                name = rootButton.name,\n                                key = rootButton.key,\n                                type = rootButton.type\n                            });\n                        }\n                        else if (rootButton.type.Equals(\"VIEW\", StringComparison.OrdinalIgnoreCase))\n                        {\n                            //URL\n                            bg.button.Add(new SingleViewButton()\n                            {\n                                name = rootButton.name,","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/JeffreySu/WeiXinMPSDK/blob/be573f6f94bdbf718dd5f6cdecb137fbc7ff651e/src/Senparc.Weixin.Work/Senparc.Weixin.Work/CommonAPIs/CommonApi.Menu.cs#L226-L262","documentation":"GetMenuFromJsonResult throws WeixinMenuException '单击按钮的key不能为空！' when parsing a menu whose root-level button has type CLICK but an empty/missing key, or a null type. In the WeChat Work menu model, click buttons must carry a non-empty key that the server echoes back in click events. The library validates this during JSON-to-object conversion and refuses invalid menu definitions.","triggerScenarios":"GetMenuFromJsonResult (called by GetMenu) parses a menu definition where a top-level button is type 'click' (case-insensitive) but button.key is null or empty, or button.type is null entirely.","commonSituations":"Hand-crafted or manually edited menu JSON in the WeChat Work console missing the key field; migrations from another WeChat product where click buttons used url instead of key; deserializing API responses from a misconfigured menu.","solutions":["Add a non-empty 'key' to every root-level button of type 'click' in the menu definition.","If the button should navigate, change type from 'click' to 'view' with a url instead of key.","Validate menu buttons before publishing/querying: check type=='click' implies key is non-empty.","Fix the menu in the WeChat Work admin console if the bad definition came from there."],"exampleFix":"// before\nnew MenuButton { type = \"click\", name = \"订单\" } // no key\n\n// after\nnew MenuButton { type = \"click\", name = \"订单\", key = \"ORDER_QUERY\" }","handlingStrategy":"validation","validationCode":"static void ValidateClickKey(IEnumerable<MenuButton> buttons)\n{\n    foreach (var b in buttons)\n        if (b.type?.Equals(\"click\", StringComparison.OrdinalIgnoreCase) == true\n            && string.IsNullOrEmpty(b.key))\n            throw new ArgumentException($\"CLICK button '{b.name}' needs a key\");\n}","typeGuard":"bool IsValidClickButton(MenuButton b) => !b.type?.Equals(\"CLICK\", StringComparison.OrdinalIgnoreCase) == true || !string.IsNullOrEmpty(b.key);","tryCatchPattern":"try { var result = CommonApi.GetMenu(token, agentId); }\ncatch (WeixinMenuException ex) { /* ex.Message: 单击按钮的key不能为空！ */ }","preventionTips":["Every CLICK button (root level) must have a non-empty key","Use 'view' + url for link buttons instead of keyless clicks","Add menu validation to your build/CI when menus are generated from config","Keep menu definitions in one place with schema validation"],"tags":["wechat-work","menu","validation","weixinmenusexception"],"backgroundTag":"schema-validation-failed","analyzedSha":"be573f6f94bdbf718dd5f6cdecb137fbc7ff651e","analyzedAt":"2026-09-12T10:01:50.733Z","contentChangedAt":"2026-09-12T10:01:50.733Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}