JeffreySu/WeiXinMPSDK · error · UnRegisterAppIdException

此appId尚未注册,请先使用WxCardApiTicketContainer.Register完成注册(全局执行一次即…

Error message

此appId尚未注册,请先使用WxCardApiTicketContainer.Register完成注册(全局执行一次即可)!

What it means

WxCardApiTicketContainer caches WeChat card api_tickets per appId, but only for appIds explicitly registered via WxCardApiTicketContainer.Register(appId, ...). GetWxCardApiTicketResult checks CheckRegistered(appId) first and throws UnRegisterAppIdException when the appId was never registered. Registration populates the container's credential/config needed to obtain and cache the ticket.

Solutions

  1. Before requesting the ticket, register the appId once at application startup: WxCardApiTicketContainer.Register(appId, appSecret, name/credential config).
  2. Guard the call with WxCardApiTicketContainer.CheckRegistered(appId) and register on demand if it returns false.
  3. Verify the registration code path actually executes in this process (e.g. Application_Start / Program startup), not only in another environment.
  4. If using distributed cache, ensure the registration's credential data is reachable from all nodes, or register on each node.

Example fix

// before
var ticket = WxCardApiTicketContainer.GetWxCardApiTicketResult(appId);
// after
// Program/App startup, run once:
WxCardApiTicketContainer.Register(appId, appSecret);
var ticket = WxCardApiTicketContainer.GetWxCardApiTicketResult(appId);
Defensive patterns

Strategy: validation

Validate before calling

if (!WxCardApiTicketContainer.CheckRegistered(appId))
{
    WxCardApiTicketContainer.Register(appId, appSecret);
}

Try / catch

try
{
    var ticket = WxCardApiTicketContainer.GetWxCardApiTicketResult(appId);
}
catch (UnRegisterAppIdException ex)
{
    // register the appId and retry once
    WxCardApiTicketContainer.Register(appId, appSecret);
    var ticket = WxCardApiTicketContainer.GetWxCardApiTicketResult(appId);
}

Prevention

When it happens

Trigger: Calling WxCardApiTicketContainer.GetWxCardApiTicketResult(appId) (or the wrapper GetWxCardApiTicket) with an appId that was never passed to WxCardApiTicketContainer.Register in the current application domain, or calling it before the registration code runs, or after an app restart that lost the registration (e.g. non-persistent container storage).

Common situations: Startup code that registers JsApiTicketContainer but forgets WxCardApiTicketContainer (they are separate containers); calling ticket APIs in a background job or new AppDomain where global registration never executed; multi-tenant apps using an appId that was registered under a different process; using GetWxCardApiTicketResultAsync without equivalent registration.

Understand the failure class

Background: "X is required", "must be set", "cannot be empty": the missing-required-config error family, from Vertex AI project/location to WeChat keys — this error's family across 18 libraries.

Related errors


AI-assisted analysis of JeffreySu/WeiXinMPSDK@be573f6f94 (2026-09-12). Data as JSON: /api/errors/b4f683037d76eca7. Report an issue: GitHub.

Appendix: source

Thrown at src/Senparc.Weixin.MP/Senparc.Weixin.MP/Containers/WxCardApiTicketContainer.cs:152

        /// <param name="appId"></param>
        /// <param name="getNewTicket">是否强制重新获取新的Ticket</param>
        /// <returns></returns>
        public static string GetWxCardApiTicket(string appId, bool getNewTicket = false)
        {
            return GetWxCardApiTicketResult(appId, getNewTicket).ticket;
        }

        /// <summary>
        /// 获取可用Ticket
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="getNewTicket">是否强制重新获取新的Ticket</param>
        /// <returns></returns>
        public static JsApiTicketResult GetWxCardApiTicketResult(string appId, bool getNewTicket = false)
        {
            if (!CheckRegistered(appId))
            {
                throw new UnRegisterAppIdException(null, "此appId尚未注册,请先使用WxCardApiTicketContainer.Register完成注册(全局执行一次即可)!");
            }

            WxCardApiTicketBag wxCardApiTicketBag = TryGetItem(appId);
            using (Cache.BeginCacheLock(LockResourceName, appId))//同步锁
            {
                wxCardApiTicketBag = TryGetItem(appId);//获锁后重新读取并二次检查过期状态
                if (getNewTicket || wxCardApiTicketBag.WxCardApiTicketExpireTime <= SystemTime.Now)
                {
                    //已过期,重新获取
                    wxCardApiTicketBag.WxCardApiTicketResult = CommonApi.GetTicket(wxCardApiTicketBag.AppId,
                                                                                   wxCardApiTicketBag.AppSecret,
                                                                                   "wx_card");
                    wxCardApiTicketBag.WxCardApiTicketExpireTime = ApiUtility.GetExpireTime(wxCardApiTicketBag.WxCardApiTicketResult.expires_in);
                    Update(wxCardApiTicketBag, null);
                }
            }
            return wxCardApiTicketBag.WxCardApiTicketResult;
        }

View on GitHub (pinned to be573f6f94)