binarywang/WxJava · error · WxErrorException
通讯录同步secret未配置
Error message
通讯录同步secret未配置
What it means
Thrown (as checked WxErrorException) by getContactAccessToken() in the default WxCpServiceImpl when the contact-sync secret is null or blank. This is the base/default HTTP client implementation. The contact secret is a separate credential from the main corp secret, used exclusively for contact-sync APIs.
Source
Thrown at weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceImpl.java:89
}
@Override
public String getContactAccessToken(boolean forceRefresh) throws WxErrorException {
final WxCpConfigStorage configStorage = getWxCpConfigStorage();
if (!configStorage.isContactAccessTokenExpired() && !forceRefresh) {
return configStorage.getContactAccessToken();
}
Lock lock = configStorage.getContactAccessTokenLock();
lock.lock();
try {
// 拿到锁之后,再次判断一下最新的token是否过期,避免重刷
if (!configStorage.isContactAccessTokenExpired() && !forceRefresh) {
return configStorage.getContactAccessToken();
}
// 使用通讯录同步secret获取access_token
String contactSecret = configStorage.getContactSecret();
if (contactSecret == null || contactSecret.trim().isEmpty()) {
throw new WxErrorException("通讯录同步secret未配置");
}
String url = String.format(configStorage.getApiUrl(WxCpApiPathConsts.GET_TOKEN),
this.configStorage.getCorpId(), contactSecret);
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.updateContactAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
} catch (IOException e) {View on GitHub (pinned to 1c43293a3c)
Solutions
- Call configStorage.setContactSecret("...") during application startup before any contact-sync API is used
- In Spring Boot starter, set wx.cp.contact-secret in application.yml or application.properties
- If using a custom config storage (e.g., Redis), ensure the contact secret is persisted and reloaded correctly
- Differentiate the contact-sync secret from the application secret — they are separate values in the WeChat admin console
Example fix
// before
WxCpDefaultConfigImpl config = new WxCpDefaultConfigImpl();
config.setCorpId(corpId);
config.setCorpSecret(corpSecret);
// missing: config.setContactSecret(...)
// after
config.setContactSecret(System.getenv("WX_CP_CONTACT_SECRET")); Defensive patterns
Strategy: validation
Validate before calling
// Validate contact secret at startup for default impl
String contactSecret = configStorage.getContactSecret();
if (StringUtils.isBlank(contactSecret)) {
log.error("通讯录同步 secret 未配置,通讯录相关接口将不可用");
// Optionally throw to fail-fast
} Prevention
- Set contactSecret via configStorage.setContactSecret() during initialization
- For Spring Boot starter, use wx.cp.contact-secret in application.yml
- The contact secret is different from corpSecret — obtain it from 管理工具 → 通讯录同步 in the admin console
- If using Redis-backed config storage, ensure the secret survives restarts
When it happens
Trigger: Any contact-sync API call (user/department/tag sync) through the default WxCpServiceImpl when configStorage.getContactSecret() returns null or empty.
Common situations: Config initialized with only corpId + corpSecret but no contactSecret; the contact secret env var is undefined; using a Redis-backed config storage that lost the contact secret after a flush; the WeChat Work admin console was reconfigured and the old secret is no longer valid (though this would cause a different error — this specific error is only about the value being absent).
Related errors
AI-assisted analysis of binarywang/WxJava@1c43293a3c (2026-08-14).
Data as JSON: /api/errors/7f346f84d5ed886e.
Report an issue: GitHub.