{"record":{"id":"cda0843f951d7bca","repo":"JeffreySu/WeiXinMPSDK","slug":"key","errorCode":null,"errorMessage":"单击按钮的key不能为空！","messagePattern":"单击按钮的key不能为空！","errorType":"exception","errorClass":"WeixinMenuException","httpStatus":null,"severity":"error","filePath":"src/Senparc.Weixin.MP/Senparc.Weixin.MP/CommonAPIs/Menu/CommonApi.Menu.Common.cs","lineNumber":109,"sourceCode":"        private static void GetButtonGroup(List<MenuFull_RootButton> rootButtonList, ButtonGroupBase buttonGroup)\n        {\n            foreach (var rootButton in rootButtonList)\n            {\n                if (rootButton == null || string.IsNullOrEmpty(rootButton.name))\n                {\n                    continue; //没有设置一级菜单\n                }\n                var availableSubButton = rootButton.sub_button == null\n                    ? 0\n                    : rootButton.sub_button.Count(z => z != null && !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                        buttonGroup.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                        buttonGroup.button.Add(new SingleViewButton()\n                        {\n                            name = rootButton.name,","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/JeffreySu/WeiXinMPSDK/blob/be573f6f94bdbf718dd5f6cdecb137fbc7ff651e/src/Senparc.Weixin.MP/Senparc.Weixin.MP/CommonAPIs/Menu/CommonApi.Menu.Common.cs#L91-L127","documentation":"When deserializing a Weixin menu response, GetButtonGroup requires a bottom-level button of type CLICK to carry a non-empty key. A null/missing type or a CLICK button with an empty key throws WeixinMenuException '单击按钮的key不能为空！'.","triggerScenarios":"Calling GetMenu/GetMenuFromJsonResult on a menu JSON where a button has type \"CLICK\" but no key field, or a button with a null type.","commonSituations":"Menus created manually in the Weixin console without setting the click key, older menus saved before key became mandatory, or third-party tools generating malformed menu JSON.","solutions":["Open the Weixin admin console and set a key for every CLICK-type button","Update the menu via the API so each click button has a key","If the button should not be a click button, change its type (e.g. view/miniprogram)","Sanitize menu JSON before calling GetMenuFromJsonResult"],"exampleFix":"// before\n{ \"type\": \"CLICK\", \"name\": \"帮助\" } // no key\n// after\n{ \"type\": \"CLICK\", \"name\": \"帮助\", \"key\": \"HELP_CLICKED\" }","handlingStrategy":"validation","validationCode":"foreach (var btn in menuJson.button)\n    if (string.Equals(btn.type, \"CLICK\", StringComparison.OrdinalIgnoreCase) && string.IsNullOrEmpty(btn.key))\n        throw new ArgumentException($\"Button '{btn.name}' is CLICK but has no key\");","typeGuard":null,"tryCatchPattern":"try\n{\n    var menu = CommonApi.GetMenu(appId);\n}\ncatch (WeixinMenuException ex)\n{\n    logger.LogError(ex, \"Menu JSON invalid: {Msg}\", ex.Message);\n}","preventionTips":["Require a key when defining CLICK buttons in your menu tooling","Validate menu JSON before pushing or fetching","Review menus created manually in the Weixin console","Pin a shared menu schema in your team's tooling"],"tags":["weixin","menu","validation"],"backgroundTag":"missing-required-argument","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"}