JeffreySu/WeiXinMPSDK · error · WeixinWorkException

UN_REGISTER_ALERT

Error message

UN_REGISTER_ALERT

What it means

JsApiTicketContainer.GetTicketResult throws WeixinWorkException with the UN_REGISTER_ALERT message ("此AppId尚未注册,JsApiTicketContainer.Register完成注册(全局执行一次即可)!") when CheckRegistered(appKey) returns false — i.e. no ticket bag exists in cache for the appId+appSecret (+isAgentConfig) key. The Senparc containers require an explicit one-time Register call at application startup before any Get* method can be used. GetTicket (its caller) surfaces the same exception.

Solutions

  1. Call JsApiTicketContainer.Register(appId, appSecret, isAgentConfig) once at application startup (e.g. in Program.cs/Startup) before serving requests.
  2. Ensure the appId/appSecret/isAgentConfig passed to GetTicket exactly match the values used at Register.
  3. In multi-tenant scenarios, register each tenant's app when it is first seen (CheckRegistered then Register).
  4. If using an external/distributed cache, re-run registration on cache restarts or register in a background initializer that retries.

Example fix

// before
var ticket = JsApiTicketContainer.GetTicket(appId, appSecret, false); // throws if not registered
// after
if (!JsApiTicketContainer.CheckRegistered(appId, appSecret, false))
{
    JsApiTicketContainer.Register(appId, appSecret, false);
}
var ticket = JsApiTicketContainer.GetTicket(appId, appSecret, false);
Defensive patterns

Strategy: validation

Validate before calling

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

Try / catch

try
{
    var ticket = JsApiTicketContainer.GetTicket(appId, appSecret, isAgentConfig);
}
catch (WeixinWorkException ex) when (ex.Message.Contains("尚未注册"))
{
    log.LogError(ex, "JS-SDK app {AppId} not registered with JsApiTicketContainer; register at startup.", appId);
    JsApiTicketContainer.Register(appId, appSecret, isAgentConfig);
}

Prevention

When it happens

Trigger: Calling JsApiTicketContainer.GetTicket/GetTicketResult before JsApiTicketContainer.Register(appId, appSecret, isAgentConfig) was executed; cache was flushed/expired and registration was not repeated; registering with a different appId/appSecret/isAgentConfig combination than the one queried.

Common situations: Forgot the startup registration after adding JS-SDK pages; multi-tenant apps where a new tenant's app is queried without registering it; distributed cache cleared (Redis restart) while registration only runs once at boot; appSecret changed so the key no longer matches.

Related errors


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

Appendix: source

Thrown at src/Senparc.Weixin.Work/Senparc.Weixin.Work/Containers/JsApiTicketContainer.cs:197

        /// <returns></returns>
        public static string GetTicket(string appId, string appSecret, bool isAgentConfig, bool getNewTicket = false)
        {
            return GetTicketResult(appId, appSecret, isAgentConfig, getNewTicket).ticket;
        }

        /// <summary>
        /// 获取可用Ticket
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="getNewTicket">是否强制重新获取新的Ticket</param>
        /// <param name="isAgentConfig">是否为“应用jsapi_ticket”,如果是某个特定应用,则输入 true(用于计算 agentConfig 的签名),否则为 false。参考:<see href="https://developer.work.weixin.qq.com/document/path/90506#14924"/></param>
        /// <returns></returns>
        public static JsApiTicketResult GetTicketResult(string appId, string appSecret, bool isAgentConfig, bool getNewTicket = false)
        {
            var appKey = AccessTokenContainer.BuildingKey(appId, appSecret,isAgentConfig);
            if (!CheckRegistered(appKey))
            {
                throw new WeixinWorkException(UN_REGISTER_ALERT);
            }

            var jsApiTicketBag = TryGetItem(appKey);
            using (Cache.BeginCacheLock(LockResourceName, appKey))
            {
                jsApiTicketBag = TryGetItem(appKey);//获锁后重新读取并二次检查过期状态
                if (getNewTicket || jsApiTicketBag.ExpireTime <= SystemTime.Now)
                {
                    //已过期,重新获取
                    jsApiTicketBag.JsApiTicketResult = CommonApi.GetTicket(jsApiTicketBag.CorpId, jsApiTicketBag.CorpSecret, isAgentConfig);
                    jsApiTicketBag.ExpireTime = ApiUtility.GetExpireTime(jsApiTicketBag.JsApiTicketResult.expires_in);
                    Update(jsApiTicketBag, null);//更新到缓存
                }
            }
            return jsApiTicketBag.JsApiTicketResult;
        }

        ///// <summary>

View on GitHub (pinned to be573f6f94)