{"record":{"id":"27f9756e4a5666bd","repo":"JeffreySu/WeiXinMPSDK","slug":"appid-code-oauthaccesstokencontainer-register","errorCode":null,"errorMessage":"此 appId code 尚未注册，请先使用 OAuthAccessTokenContainer.Register 完成注册（全局执行一次即可）！","messagePattern":"此 appId code 尚未注册，请先使用 OAuthAccessTokenContainer\\.Register 完成注册（全局执行一次即可）！","errorType":"exception","errorClass":"UnRegisterAppIdException","httpStatus":null,"severity":"error","filePath":"src/Senparc.Weixin.MP/Senparc.Weixin.MP/Containers/OAuthAccessTokenContainer.cs","lineNumber":227,"sourceCode":"        /// <returns></returns>\n        public static string GetOAuthAccessToken(string appId, string code, bool getNewToken = false)\n        {\n            return GetOAuthAccessTokenResult(appId, code, getNewToken).access_token;\n        }\n\n        /// <summary>\n        /// 获取可用Ticket\n        /// </summary>\n        /// <param name=\"appId\"></param>\n        /// <param name=\"code\">code作为换取access_token的票据，每次用户授权带上的code将不一样，code只能使用一次，5分钟未被使用自动过期。</param>\n        /// <param name=\"getNewToken\">是否强制重新获取新的Ticket</param>\n        /// <returns></returns>\n        public static OAuthAccessTokenResult GetOAuthAccessTokenResult(string appId, string code, bool getNewToken = false)\n        {\n            var key = BuildKey(appId, code);\n            if (!CheckRegistered(key))\n            {\n                throw new UnRegisterAppIdException(null, \"此 appId code 尚未注册，请先使用 OAuthAccessTokenContainer.Register 完成注册（全局执行一次即可）！\");\n            }\n\n            var oAuthAccessTokenBag = TryGetItem(key);\n            using (Cache.BeginCacheLock(LockResourceName, key))//同步锁\n            {\n                oAuthAccessTokenBag = TryGetItem(key);//获锁后重新读取并二次检查过期状态\n                if (getNewToken || oAuthAccessTokenBag.OAuthAccessTokenExpireTime <= SystemTime.Now)\n                {\n                    //已过期，重新获取\n                    oAuthAccessTokenBag.OAuthAccessTokenResult = OAuthApi.GetAccessToken(oAuthAccessTokenBag.AppId, oAuthAccessTokenBag.AppSecret, code);\n                    oAuthAccessTokenBag.OAuthAccessTokenExpireTime =\n                        ApiUtility.GetExpireTime(oAuthAccessTokenBag.OAuthAccessTokenResult.expires_in);\n                    Update(oAuthAccessTokenBag, null);\n                }\n            }\n            return oAuthAccessTokenBag.OAuthAccessTokenResult;\n        }\n","sourceCodeStart":209,"sourceCodeEnd":245,"githubUrl":"https://github.com/JeffreySu/WeiXinMPSDK/blob/be573f6f94bdbf718dd5f6cdecb137fbc7ff651e/src/Senparc.Weixin.MP/Senparc.Weixin.MP/Containers/OAuthAccessTokenContainer.cs#L209-L245","documentation":"GetOAuthAccessTokenResult throws UnRegisterAppIdException when CheckRegistered(key) is false, where key = BuildKey(appId, code). OAuth access tokens are keyed by appId+code pairs, and each pair must be registered via OAuthAccessTokenContainer.Register before retrieval; this container is separate from AccessTokenContainer.","triggerScenarios":"Calling OAuthAccessTokenContainer.GetOAuthAccessToken(appId, code) (or GetOAuthAccessTokenResult) for an appId+code pair never registered via OAuthAccessTokenContainer.Register, or after the pair's registration expired/was evicted from cache.","commonSituations":"Assuming MP token registration covers OAuth (separate container); OAuth flow in a different server/process than the one that registered; reusing a container across restarts with memory cache; forgetting to register after obtaining the OAuth code from WeChat's redirect.","solutions":["Call OAuthAccessTokenContainer.Register(appId, appSecret) once at startup so appId+code lookups find a registration.","Ensure the same process/cache that handles the OAuth callback performs the registration.","Check BuildKey output consistency: the appId used at lookup must match the registered one.","With memory cache, re-run registration on every application start."],"exampleFix":"// before\nvar token = OAuthAccessTokenContainer.GetOAuthAccessToken(appId, code); // throws\n// after\nOAuthAccessTokenContainer.Register(appId, appSecret); // at startup\nvar token = OAuthAccessTokenContainer.GetOAuthAccessToken(appId, code);","handlingStrategy":"try-catch","validationCode":"var key = BuildKey(appId, code);\nif (!OAuthAccessTokenContainer.CheckRegistered(key))\n{\n    OAuthAccessTokenContainer.Register(appId, appSecret);\n}","typeGuard":"bool IsOAuthRegistered(string appId) => OAuthAccessTokenContainer.CheckRegistered(appId);","tryCatchPattern":"try\n{\n    var token = OAuthAccessTokenContainer.GetOAuthAccessToken(appId, code);\n}\ncatch (UnRegisterAppIdException ex)\n{\n    _logger.LogError(ex, \"OAuth pair for appId {AppId} unregistered; re-registering.\", appId);\n    OAuthAccessTokenContainer.Register(appId, _secrets.Get(appId));\n}","preventionTips":["Register OAuth container at startup in the same process that serves the WeChat OAuth callback.","Use Redis or another shared cache for multi-instance deployments so registrations are visible everywhere.","Never assume MP access-token registration covers the OAuth container; they are independent.","Re-register on every start when memory cache is used."],"tags":["csharp","weixin","oauth","unregistered-appid"],"backgroundTag":"unregistered-appid","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"}