binarywang/WxJava · error · WxErrorException

通讯录同步secret未配置

Error message

通讯录同步secret未配置

What it means

Thrown (as checked WxErrorException) by getContactAccessToken() in the Apache HttpClient implementation when the contact-sync secret (通讯录同步secret) is null or blank. This secret is required to obtain a separate access_token for contact-sync API calls, distinct from the main corp secret.

Source

Thrown at weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceApacheHttpClientImpl.java:94

  }

  @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未配置");
      }
      String url = String.format(this.configStorage.getApiUrl(WxCpApiPathConsts.GET_TOKEN),
        this.configStorage.getCorpId(), contactSecret);

      try {
        HttpGet httpGet = new HttpGet(url);
        if (this.httpProxy != null) {
          RequestConfig config = RequestConfig.custom()
            .setProxy(this.httpProxy).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);

View on GitHub (pinned to 1c43293a3c)

Solutions

  1. Set the contact secret via configStorage.setContactSecret("your-contact-sync-secret") during initialization
  2. In Spring Boot starter, set wx.cp.contact-secret in application.yml
  3. Ensure the secret value comes from a reliable source (env var, config center) that is present in all environments
  4. Verify the secret is the 通讯录同步 secret from the WeChat Work admin console, not the main application secret

Example fix

// before
WxCpConfigStorage config = new WxCpDefaultConfigImpl();
config.setCorpId("corp123");
config.setCorpSecret("app-secret");
// contactSecret never set → getContactAccessToken fails

// after
WxCpDefaultConfigImpl config = new WxCpDefaultConfigImpl();
config.setCorpId("corp123");
config.setCorpSecret("app-secret");
config.setContactSecret(System.getenv("WX_CP_CONTACT_SECRET"));
Defensive patterns

Strategy: validation

Validate before calling

// Validate contact secret is configured at startup
String contactSecret = configStorage.getContactSecret();
if (StringUtils.isBlank(contactSecret)) {
  throw new IllegalStateException("启动检查失败:通讯录同步 secret 未配置,请在企业微信管理后台获取并设置 contactSecret");
}

Prevention

When it happens

Trigger: Calling any method that triggers getContactAccessToken(true) — typically contact/department/user sync operations — when configStorage.getContactSecret() returns null or an empty/whitespace string.

Common situations: The WxCpConfigStorage was initialized without calling setContactSecret(); the secret was set from an environment variable that is not defined in the current deployment; the secret was cleared during a config refresh; using a config storage that does not persist the contact secret across restarts.

Related errors


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