binarywang/WxJava · error · WxErrorException

通讯录同步secret未配置

Error message

通讯录同步secret未配置

What it means

Thrown (as checked WxErrorException) by getContactAccessToken() in the Jodd HTTP implementation when the contact-sync secret is null or blank. Identical validation logic to the other service implementations, specific to the Jodd HTTP client variant.

Solutions

  1. Set the contact secret via configStorage.setContactSecret() regardless of which HTTP client implementation you use
  2. Ensure wx.cp.contact-secret is in application.yml for Spring Boot with the Jodd implementation
  3. Centralize secret configuration so all HTTP client variants share the same config source

Example fix

// before — Jodd impl, contact secret missing
WxCpServiceJoddHttpImpl service = new WxCpServiceJoddHttpImpl();
service.setWxCpConfigStorage(config); // config.contactSecret == null

// after
config.setContactSecret(System.getenv("WX_CP_CONTACT_SECRET"));
service.setWxCpConfigStorage(config);
Defensive patterns

Strategy: validation

Validate before calling

// Validate contact secret for Jodd impl
String contactSecret = configStorage.getContactSecret();
if (StringUtils.isBlank(contactSecret)) {
  throw new IllegalStateException("通讯录同步 secret 未配置");
}

Prevention

When it happens

Trigger: Any contact-sync API call via WxCpServiceJoddHttpImpl when configStorage.getContactSecret() returns null or whitespace.

Common situations: Switched to the Jodd HTTP client implementation without copying the contact secret into the config; env var missing in the deployment that uses Jodd; config storage that does not persist the contact secret.

Related errors


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

Appendix: source

Thrown at weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceJoddHttpImpl.java:84

  }

  @Override
  public String getContactAccessToken(boolean forceRefresh) throws WxErrorException {
    if (!this.configStorage.isContactAccessTokenExpired() && !forceRefresh) {
      return this.configStorage.getContactAccessToken();
    }

    Lock lock = this.configStorage.getContactAccessTokenLock();
    lock.lock();
    try {
      // 拿到锁之后,再次判断一下最新的token是否过期,避免重刷
      if (!this.configStorage.isContactAccessTokenExpired() && !forceRefresh) {
        return this.configStorage.getContactAccessToken();
      }
      // 使用通讯录同步secret获取access_token
      String contactSecret = this.configStorage.getContactSecret();
      if (contactSecret == null || contactSecret.trim().isEmpty()) {
        throw new WxErrorException("通讯录同步secret未配置");
      }
      HttpRequest request = HttpRequest.get(String.format(this.configStorage.getApiUrl(WxCpApiPathConsts.GET_TOKEN),
        this.configStorage.getCorpId(), contactSecret));
      if (this.httpProxy != null) {
        httpClient.useProxy(this.httpProxy);
      }
      request.withConnectionProvider(httpClient);
      HttpResponse response = request.send();

      String resultContent = response.bodyText();
      WxError error = WxError.fromJson(resultContent, WxType.CP);
      if (error.getErrorCode() != 0) {
        throw new WxErrorException(error);
      }
      WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
      this.configStorage.updateContactAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
    } finally {
      lock.unlock();

View on GitHub (pinned to 1c43293a3c)