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
- Before requesting the ticket, register the appId once at application startup: WxCardApiTicketContainer.Register(appId, appSecret, name/credential config).
- Guard the call with WxCardApiTicketContainer.CheckRegistered(appId) and register on demand if it returns false.
- Verify the registration code path actually executes in this process (e.g. Application_Start / Program startup), not only in another environment.
- 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
- Register every appId for ALL required containers (AccessToken, JsApiTicket, WxCardApiTicket) in a single startup routine.
- Use a central configuration loop that registers each configured appId once at app start.
- Write a unit/integration test that calls CheckRegistered for every appId in config before ticket APIs are used.
- Document that container registration is per-process and must be repeated after restarts.
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
- 未知的Event下属请求信息
- DefaultMessageHandlerAsyncEvent 类型未作处理:
- 未知的Event下属请求信息
- 注册委托容量必须大于 0。
- 当前已有 个注册委托,不能把容量降低到 。
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)