binarywang/WxJava · error · WxErrorException

会话存档secret未配置

Error message

会话存档secret未配置

What it means

Thrown (as checked WxErrorException) by getMsgAuditAccessToken() in the default WxCpServiceImpl when the chat-archive secret is null or blank. The msg-audit secret is a dedicated credential for the 会话存档 feature, separate from both the corp secret and the contact secret.

Source

Thrown at weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceImpl.java:132

  }

  @Override
  public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException {
    final WxCpConfigStorage configStorage = getWxCpConfigStorage();
    if (!configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) {
      return configStorage.getMsgAuditAccessToken();
    }
    Lock lock = configStorage.getMsgAuditAccessTokenLock();
    lock.lock();
    try {
      // 拿到锁之后,再次判断一下最新的token是否过期,避免重刷
      if (!configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) {
        return configStorage.getMsgAuditAccessToken();
      }
      // 使用会话存档secret获取access_token
      String msgAuditSecret = configStorage.getMsgAuditSecret();
      if (msgAuditSecret == null || msgAuditSecret.trim().isEmpty()) {
        throw new WxErrorException("会话存档secret未配置");
      }
      String url = String.format(configStorage.getApiUrl(WxCpApiPathConsts.GET_TOKEN),
        this.configStorage.getCorpId(), msgAuditSecret);
      try {
        HttpGet httpGet = new HttpGet(url);
        if (getRequestHttpProxy() != null) {
          RequestConfig config = RequestConfig.custom().setProxy(getRequestHttpProxy()).build();
          httpGet.setConfig(config);
        }
        String resultContent = getRequestHttpClient().execute(httpGet, ApacheBasicResponseHandler.INSTANCE);
        WxError error = WxError.fromJson(resultContent, WxType.CP);
        if (error.getErrorCode() != 0) {
          throw new WxErrorException(error);
        }

        WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
        configStorage.updateMsgAuditAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
      } catch (IOException e) {

View on GitHub (pinned to 1c43293a3c)

Solutions

  1. Call configStorage.setMsgAuditSecret("...") during initialization
  2. In Spring Boot, set wx.cp.msg-audit-secret in application.yml
  3. Enable the 会话存档 feature in the WeChat Work admin console and copy the secret
  4. Also set msgAuditPriKey — decryption will fail without it even if the token succeeds

Example fix

// before
config.setCorpId(corpId);
config.setCorpSecret(corpSecret);
// msgAuditSecret and msgAuditPriKey missing

// after
config.setMsgAuditSecret(System.getenv("WX_CP_MSG_AUDIT_SECRET"));
config.setMsgAuditPriKey(System.getenv("WX_CP_MSG_AUDIT_PRI_KEY"));
Defensive patterns

Strategy: validation

Validate before calling

// Validate msg-audit secret and private key at startup
String secret = configStorage.getMsgAuditSecret();
String priKey = configStorage.getMsgAuditPriKey();
if (StringUtils.isBlank(secret) || StringUtils.isBlank(priKey)) {
  throw new IllegalStateException("会话存档功能需要同时配置 msgAuditSecret 和 msgAuditPriKey");
}

Prevention

When it happens

Trigger: Any msg-audit operation (chat records, media files, agreement info) through the default WxCpServiceImpl when configStorage.getMsgAuditSecret() is null or empty.

Common situations: Config object created without calling setMsgAuditSecret(); the 会话存档 feature is not enabled in the WeChat admin console; the secret env var is absent in production; using WxCpInMemoryConfigStorage which loses state on restart.

Related errors


AI-assisted analysis of binarywang/WxJava@1c43293a3c (2026-08-14). Data as JSON: /api/errors/88847b0dad1f16fd. Report an issue: GitHub.