binarywang/WxJava · error · WxErrorException
线程 [{}] 获取会话存档SDK失败,请检查是否已调用 closeAllSdks()
Error message
线程 [{}] 获取会话存档SDK失败,请检查是否已调用 closeAllSdks() What it means
Thrown as WxErrorException (checked) in getOrInitThreadLocalSdk when the ThreadLocal contains a non-null SDK handle that is no longer in the managedSdks set. This means closeAllSdks() was called (likely by another thread) which destroyed and unregistered all SDK handles, but the current thread's ThreadLocal still holds a stale reference to an already-destroyed native SDK. After logging a warning and removing the stale handle, the method refuses to proceed and throws.
Source
Thrown at weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMsgAuditServiceImpl.java:89
}
/**
* 获取当前线程的 SDK,不存在则初始化。
* SDK 在线程内跨调用复用,无需每次重新初始化。
*
* @return sdk id
* @throws WxErrorException 初始化失败时抛出异常
*/
private long getOrInitThreadLocalSdk() throws WxErrorException {
Long sdk = threadLocalSdk.get();
if (sdk != null && sdk > 0) {
// 校验句柄是否仍受管理:closeAllSdks() 后其他线程 ThreadLocal 可能保留已销毁的 id
if (managedSdks.contains(sdk)) {
return sdk;
}
log.warn("线程 [{}] 发现已失效的会话存档SDK句柄 sdk={},请检查调用逻辑", Thread.currentThread().getName(), sdk);
threadLocalSdk.remove();
throw new WxErrorException("线程 [" + Thread.currentThread().getName() + "] 获取会话存档SDK失败,请检查是否已调用 closeAllSdks()");
}
long newSdk = createSdk();
threadLocalSdk.set(newSdk);
managedSdks.add(newSdk);
log.info("线程 [{}] 初始化会话存档SDK成功,sdk={}", Thread.currentThread().getName(), newSdk);
return newSdk;
}
/**
* 创建并初始化一个新的会话存档 SDK 实例。
* <p>通常通过 {@link #getOrInitThreadLocalSdk()} 间接调用以复用 ThreadLocal 中的实例;
* 旧版直接暴露 sdk 的 API(如 {@link #getChatDatas})也会直接调用本方法,此时 SDK 由调用方自行管理。</p>
* <p>Finance.loadingLibraries() 底层依赖 System.load(),JVM 保证同一库不重复加载,多线程并发调用安全。</p>
*/
private long createSdk() throws WxErrorException {
WxCpConfigStorage configStorage = cpService.getWxCpConfigStorage();
String configPath = configStorage.getMsgAuditLibPath();View on GitHub (pinned to 1c43293a3c)
Solutions
- After calling closeAllSdks(), do not reuse threads that previously held SDK handles without clearing their state. Alternatively, restart the thread pool.
- Catch this specific error and retry: the exception text contains '获取会话存档SDK失败'. On retry, getOrInitThreadLocalSdk will create a fresh SDK since the ThreadLocal was cleared.
- Review the lifecycle: only call closeAllSdks() during full application shutdown, not during runtime config refresh.
Example fix
// before
cpService.getMsgAuditService().closeAllSdks();
// thread pool reuses threads; next call on a stale thread throws
cpService.getMsgAuditService().getDecryptData(sdk, chatData, 1);
// after - catch and retry since ThreadLocal is cleared after throw
try {
cpService.getMsgAuditService().getDecryptData(sdk, chatData, 1);
} catch (WxErrorException e) {
if (e.getMessage().contains("获取会话存档SDK失败")) {
// ThreadLocal was cleared, retry will init fresh SDK
cpService.getMsgAuditService().getDecryptData(sdk, chatData, 1);
} else {
throw e;
}
} Defensive patterns
Strategy: retry
Try / catch
try {
cpService.getMsgAuditService().getDecryptData(sdk, chatData, pkcs1);
} catch (WxErrorException e) {
if (e.getMessage() != null && e.getMessage().contains("获取会话存档SDK失败")) {
// ThreadLocal was cleared by this exception, retry will init a fresh SDK
cpService.getMsgAuditService().getDecryptData(sdk, chatData, pkcs1);
} else {
throw e;
}
} Prevention
- Do not call closeAllSdks() during runtime; reserve it for full application shutdown.
- If using a thread pool, consider clearing ThreadLocals or restarting threads after closeAllSdks().
- Catch this specific error and retry once, since the ThreadLocal is cleared on throw.
When it happens
Trigger: Thread A calls getOrInitThreadLocalSdk() after Thread B (or the same thread in a different lifecycle phase) called closeAllSdks(). Thread A's ThreadLocal still holds the old SDK id, which is no longer tracked in managedSdks because closeAllSdks removed it. This is a lifecycle management issue, not a normal runtime error.
Common situations: Application uses a thread pool (e.g., Tomcat request threads, scheduled executor) where threads are reused. After closeAllSdks() is called (e.g., during shutdown or config refresh), pooled threads that previously used the SDK still hold the destroyed handle in their ThreadLocal. On the next use, this error fires.
Related errors
- getchatdata err ret {}
- 请配置会话存档sdk文件的路径,不要配错了!!
- 请仔细配置会话存档文件路径!!
- init sdk err ret {}
- 请配置会话存档私钥【msgAuditPriKey】
AI-assisted analysis of binarywang/WxJava@1c43293a3c (2026-08-14).
Data as JSON: /api/errors/035af7973dedb3ff.
Report an issue: GitHub.