{"record":{"id":"3b65491de9e63bf4","repo":"JeffreySu/WeiXinMPSDK","slug":"un-register-alert","errorCode":null,"errorMessage":"UN_REGISTER_ALERT","messagePattern":"UN_REGISTER_ALERT","errorType":"exception","errorClass":"WeixinWorkException","httpStatus":null,"severity":"error","filePath":"src/Senparc.Weixin.Work/Senparc.Weixin.Work/Containers/JsApiTicketContainer.cs","lineNumber":197,"sourceCode":"        /// <returns></returns>\n        public static string GetTicket(string appId, string appSecret, bool isAgentConfig, bool getNewTicket = false)\n        {\n            return GetTicketResult(appId, appSecret, isAgentConfig, getNewTicket).ticket;\n        }\n\n        /// <summary>\n        /// 获取可用Ticket\n        /// </summary>\n        /// <param name=\"appId\"></param>\n        /// <param name=\"getNewTicket\">是否强制重新获取新的Ticket</param>\n        /// <param name=\"isAgentConfig\">是否为“应用jsapi_ticket”，如果是某个特定应用，则输入 true（用于计算 agentConfig 的签名），否则为 false。参考：<see href=\"https://developer.work.weixin.qq.com/document/path/90506#14924\"/></param>\n        /// <returns></returns>\n        public static JsApiTicketResult GetTicketResult(string appId, string appSecret, bool isAgentConfig, bool getNewTicket = false)\n        {\n            var appKey = AccessTokenContainer.BuildingKey(appId, appSecret,isAgentConfig);\n            if (!CheckRegistered(appKey))\n            {\n                throw new WeixinWorkException(UN_REGISTER_ALERT);\n            }\n\n            var jsApiTicketBag = TryGetItem(appKey);\n            using (Cache.BeginCacheLock(LockResourceName, appKey))\n            {\n                jsApiTicketBag = TryGetItem(appKey);//获锁后重新读取并二次检查过期状态\n                if (getNewTicket || jsApiTicketBag.ExpireTime <= SystemTime.Now)\n                {\n                    //已过期，重新获取\n                    jsApiTicketBag.JsApiTicketResult = CommonApi.GetTicket(jsApiTicketBag.CorpId, jsApiTicketBag.CorpSecret, isAgentConfig);\n                    jsApiTicketBag.ExpireTime = ApiUtility.GetExpireTime(jsApiTicketBag.JsApiTicketResult.expires_in);\n                    Update(jsApiTicketBag, null);//更新到缓存\n                }\n            }\n            return jsApiTicketBag.JsApiTicketResult;\n        }\n\n        ///// <summary>","sourceCodeStart":179,"sourceCodeEnd":215,"githubUrl":"https://github.com/JeffreySu/WeiXinMPSDK/blob/be573f6f94bdbf718dd5f6cdecb137fbc7ff651e/src/Senparc.Weixin.Work/Senparc.Weixin.Work/Containers/JsApiTicketContainer.cs#L179-L215","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Call JsApiTicketContainer.Register(appId, appSecret, isAgentConfig) once at application startup (e.g. in Program.cs/Startup) before serving requests.","Ensure the appId/appSecret/isAgentConfig passed to GetTicket exactly match the values used at Register.","In multi-tenant scenarios, register each tenant's app when it is first seen (CheckRegistered then Register).","If using an external/distributed cache, re-run registration on cache restarts or register in a background initializer that retries."],"exampleFix":"// before\nvar ticket = JsApiTicketContainer.GetTicket(appId, appSecret, false); // throws if not registered\n// after\nif (!JsApiTicketContainer.CheckRegistered(appId, appSecret, false))\n{\n    JsApiTicketContainer.Register(appId, appSecret, false);\n}\nvar ticket = JsApiTicketContainer.GetTicket(appId, appSecret, false);","handlingStrategy":"validation","validationCode":"if (!JsApiTicketContainer.CheckRegistered(appId, appSecret, isAgentConfig))\n{\n    JsApiTicketContainer.Register(appId, appSecret, isAgentConfig);\n}","typeGuard":null,"tryCatchPattern":"try\n{\n    var ticket = JsApiTicketContainer.GetTicket(appId, appSecret, isAgentConfig);\n}\ncatch (WeixinWorkException ex) when (ex.Message.Contains(\"尚未注册\"))\n{\n    log.LogError(ex, \"JS-SDK app {AppId} not registered with JsApiTicketContainer; register at startup.\", appId);\n    JsApiTicketContainer.Register(appId, appSecret, isAgentConfig);\n}","preventionTips":["Call Container registration for all apps in Program.cs/Startup before the app starts serving traffic.","Keep a single registry of (appId, appSecret, isAgentConfig) tuples used by both Register and Get.","Re-run registration when the cache backend restarts; don't register only in one-off scripts."],"tags":["jsapi-ticket","not-registered","startup-config"],"backgroundTag":"missing-configuration","analyzedSha":"be573f6f94bdbf718dd5f6cdecb137fbc7ff651e","analyzedAt":"2026-09-12T10:01:50.733Z","contentChangedAt":"2026-09-12T10:01:50.733Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}