{"record":{"id":"728dbb4e711a69da","repo":"binarywang/WxJava","slug":"secret-728dbb","errorCode":null,"errorMessage":"通讯录同步secret未配置","messagePattern":"通讯录同步secret未配置","errorType":"validation","errorClass":"WxErrorException","httpStatus":null,"severity":"error","filePath":"weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceJoddHttpImpl.java","lineNumber":84,"sourceCode":"  }\n\n  @Override\n  public String getContactAccessToken(boolean forceRefresh) throws WxErrorException {\n    if (!this.configStorage.isContactAccessTokenExpired() && !forceRefresh) {\n      return this.configStorage.getContactAccessToken();\n    }\n\n    Lock lock = this.configStorage.getContactAccessTokenLock();\n    lock.lock();\n    try {\n      // 拿到锁之后，再次判断一下最新的token是否过期，避免重刷\n      if (!this.configStorage.isContactAccessTokenExpired() && !forceRefresh) {\n        return this.configStorage.getContactAccessToken();\n      }\n      // 使用通讯录同步secret获取access_token\n      String contactSecret = this.configStorage.getContactSecret();\n      if (contactSecret == null || contactSecret.trim().isEmpty()) {\n        throw new WxErrorException(\"通讯录同步secret未配置\");\n      }\n      HttpRequest request = HttpRequest.get(String.format(this.configStorage.getApiUrl(WxCpApiPathConsts.GET_TOKEN),\n        this.configStorage.getCorpId(), contactSecret));\n      if (this.httpProxy != null) {\n        httpClient.useProxy(this.httpProxy);\n      }\n      request.withConnectionProvider(httpClient);\n      HttpResponse response = request.send();\n\n      String resultContent = response.bodyText();\n      WxError error = WxError.fromJson(resultContent, WxType.CP);\n      if (error.getErrorCode() != 0) {\n        throw new WxErrorException(error);\n      }\n      WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);\n      this.configStorage.updateContactAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());\n    } finally {\n      lock.unlock();","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/binarywang/WxJava/blob/1c43293a3c2c9d7e91304b6d037fb017f680d0c6/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceJoddHttpImpl.java#L66-L102","documentation":"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.","triggerScenarios":"Any contact-sync API call via WxCpServiceJoddHttpImpl when configStorage.getContactSecret() returns null or whitespace.","commonSituations":"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.","solutions":["Set the contact secret via configStorage.setContactSecret() regardless of which HTTP client implementation you use","Ensure wx.cp.contact-secret is in application.yml for Spring Boot with the Jodd implementation","Centralize secret configuration so all HTTP client variants share the same config source"],"exampleFix":"// before — Jodd impl, contact secret missing\nWxCpServiceJoddHttpImpl service = new WxCpServiceJoddHttpImpl();\nservice.setWxCpConfigStorage(config); // config.contactSecret == null\n\n// after\nconfig.setContactSecret(System.getenv(\"WX_CP_CONTACT_SECRET\"));\nservice.setWxCpConfigStorage(config);","handlingStrategy":"validation","validationCode":"// Validate contact secret for Jodd impl\nString contactSecret = configStorage.getContactSecret();\nif (StringUtils.isBlank(contactSecret)) {\n  throw new IllegalStateException(\"通讯录同步 secret 未配置\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Secret configuration must be set regardless of HTTP client implementation (Jodd, Apache, HttpComponents, default)","Centralize config setup so the secret is always populated","For Spring Boot, use wx.cp.contact-secret"],"tags":["wechat-cp","config","contact-sync","access-token","secret","jodd"],"backgroundTag":null,"analyzedSha":"1c43293a3c2c9d7e91304b6d037fb017f680d0c6","analyzedAt":"2026-08-14T02:29:11.060Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}