JeffreySu/WeiXinMPSDK · error · WeixinMenuException
单击按钮的key不能为空!
Error message
单击按钮的key不能为空!
What it means
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.
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.
Example fix
// before
new MenuButton { type = "click", name = "订单" } // no key
// after
new MenuButton { type = "click", name = "订单", key = "ORDER_QUERY" } Defensive patterns
Strategy: validation
Validate before calling
static void ValidateClickKey(IEnumerable<MenuButton> buttons)
{
foreach (var b in buttons)
if (b.type?.Equals("click", StringComparison.OrdinalIgnoreCase) == true
&& string.IsNullOrEmpty(b.key))
throw new ArgumentException($"CLICK button '{b.name}' needs a key");
} Type guard
bool IsValidClickButton(MenuButton b) => !b.type?.Equals("CLICK", StringComparison.OrdinalIgnoreCase) == true || !string.IsNullOrEmpty(b.key); Try / catch
try { var result = CommonApi.GetMenu(token, agentId); }
catch (WeixinMenuException ex) { /* ex.Message: 单击按钮的key不能为空! */ } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
AI-assisted analysis of JeffreySu/WeiXinMPSDK@be573f6f94 (2026-09-12).
Data as JSON: /api/errors/7e750860c48a3fed.
Report an issue: GitHub.
Appendix: source
Thrown at src/Senparc.Weixin.Work/Senparc.Weixin.Work/CommonAPIs/CommonApi.Menu.cs:244
try
{
//重新整理按钮信息
ButtonGroup bg = new ButtonGroup();
foreach (var rootButton in resultFull.button)
{
if (rootButton.name == null)
{
continue;//没有设置一级菜单
}
var availableSubButton = rootButton.sub_button.Count(z => !string.IsNullOrEmpty(z.name));//可用二级菜单按钮数量
if (availableSubButton == 0)
{
//底部单击按钮
if (rootButton.type == null ||
(rootButton.type.Equals("CLICK", StringComparison.OrdinalIgnoreCase)
&& string.IsNullOrEmpty(rootButton.key)))
{
throw new WeixinMenuException("单击按钮的key不能为空!");
}
if (rootButton.type.Equals("CLICK", StringComparison.OrdinalIgnoreCase))
{
//点击
bg.button.Add(new SingleClickButton()
{
name = rootButton.name,
key = rootButton.key,
type = rootButton.type
});
}
else if (rootButton.type.Equals("VIEW", StringComparison.OrdinalIgnoreCase))
{
//URL
bg.button.Add(new SingleViewButton()
{
name = rootButton.name,View on GitHub (pinned to be573f6f94)