JeffreySu/WeiXinMPSDK · error · UnRegisterAppIdException

此 appId code 尚未注册,请先使用 OAuthAccessTokenContainer.Register…

Error message

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

What it means

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.

Solutions

  1. Call OAuthAccessTokenContainer.Register(appId, appSecret) once at startup so appId+code lookups find a registration.
  2. Ensure the same process/cache that handles the OAuth callback performs the registration.
  3. Check BuildKey output consistency: the appId used at lookup must match the registered one.
  4. With memory cache, re-run registration on every application start.

Example fix

// before
var token = OAuthAccessTokenContainer.GetOAuthAccessToken(appId, code); // throws
// after
OAuthAccessTokenContainer.Register(appId, appSecret); // at startup
var token = OAuthAccessTokenContainer.GetOAuthAccessToken(appId, code);
Defensive patterns

Strategy: try-catch

Validate before calling

var key = BuildKey(appId, code);
if (!OAuthAccessTokenContainer.CheckRegistered(key))
{
    OAuthAccessTokenContainer.Register(appId, appSecret);
}

Type guard

bool IsOAuthRegistered(string appId) => OAuthAccessTokenContainer.CheckRegistered(appId);

Try / catch

try
{
    var token = OAuthAccessTokenContainer.GetOAuthAccessToken(appId, code);
}
catch (UnRegisterAppIdException ex)
{
    _logger.LogError(ex, "OAuth pair for appId {AppId} unregistered; re-registering.", appId);
    OAuthAccessTokenContainer.Register(appId, _secrets.Get(appId));
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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

Appendix: source

Thrown at src/Senparc.Weixin.MP/Senparc.Weixin.MP/Containers/OAuthAccessTokenContainer.cs:227

        /// <returns></returns>
        public static string GetOAuthAccessToken(string appId, string code, bool getNewToken = false)
        {
            return GetOAuthAccessTokenResult(appId, code, getNewToken).access_token;
        }

        /// <summary>
        /// 获取可用Ticket
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="code">code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。</param>
        /// <param name="getNewToken">是否强制重新获取新的Ticket</param>
        /// <returns></returns>
        public static OAuthAccessTokenResult GetOAuthAccessTokenResult(string appId, string code, bool getNewToken = false)
        {
            var key = BuildKey(appId, code);
            if (!CheckRegistered(key))
            {
                throw new UnRegisterAppIdException(null, "此 appId code 尚未注册,请先使用 OAuthAccessTokenContainer.Register 完成注册(全局执行一次即可)!");
            }

            var oAuthAccessTokenBag = TryGetItem(key);
            using (Cache.BeginCacheLock(LockResourceName, key))//同步锁
            {
                oAuthAccessTokenBag = TryGetItem(key);//获锁后重新读取并二次检查过期状态
                if (getNewToken || oAuthAccessTokenBag.OAuthAccessTokenExpireTime <= SystemTime.Now)
                {
                    //已过期,重新获取
                    oAuthAccessTokenBag.OAuthAccessTokenResult = OAuthApi.GetAccessToken(oAuthAccessTokenBag.AppId, oAuthAccessTokenBag.AppSecret, code);
                    oAuthAccessTokenBag.OAuthAccessTokenExpireTime =
                        ApiUtility.GetExpireTime(oAuthAccessTokenBag.OAuthAccessTokenResult.expires_in);
                    Update(oAuthAccessTokenBag, null);
                }
            }
            return oAuthAccessTokenBag.OAuthAccessTokenResult;
        }

View on GitHub (pinned to be573f6f94)