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
- 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.
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
- 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.
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
- 此appId(…
- 此appId尚未注册,请先使用JsApiTicketContainer.Register完成注册(全局执行一次即可)!
- 注册委托容量必须大于 0。
- 当前已有 个注册委托,不能把容量降低到 。
- 注册委托数量已达到上限 ,请先注销不再使用的账号或提高 MaximumCount。
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)