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

  1. Add a non-empty 'key' to every root-level button of type 'click' in the menu definition.
  2. If the button should navigate, change type from 'click' to 'view' with a url instead of key.
  3. Validate menu buttons before publishing/querying: check type=='click' implies key is non-empty.
  4. 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

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)