{"record":{"id":"8e331d70aea7b3cb","repo":"binarywang/WxJava","slug":"appid-8e331d","errorCode":null,"errorMessage":"请确保微信开放平台配置 appId 的唯一性","messagePattern":"请确保微信开放平台配置 appId 的唯一性","errorType":"validation","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/configuration/services/AbstractWxOpenConfiguration.java","lineNumber":50,"sourceCode":"  protected WxOpenMultiServices wxOpenMultiServices(WxOpenMultiProperties wxOpenMultiProperties) {\n    Map<String, WxOpenSingleProperties> appsMap = wxOpenMultiProperties.getApps();\n    if (appsMap == null || appsMap.isEmpty()) {\n      log.warn(\"微信开放平台应用参数未配置，通过 WxOpenMultiServices#getWxOpenService(\\\"tenantId\\\")获取实例将返回空\");\n      return new WxOpenMultiServicesImpl();\n    }\n    /**\n     * 校验 appId 是否唯一，避免使用 redis 缓存 token、ticket 时错乱。\n     */\n    Collection<WxOpenSingleProperties> apps = appsMap.values();\n    if (apps.size() > 1) {\n      // 校验 appId 是否唯一\n      String nullAppIdPlaceholder = \"__NULL_APP_ID__\";\n      boolean multi = apps.stream()\n        // 没有 appId，如果不判断是否为空，这里会报 NPE 异常\n        .collect(Collectors.groupingBy(c -> c.getAppId() == null ? nullAppIdPlaceholder : c.getAppId(), Collectors.counting()))\n        .entrySet().stream().anyMatch(e -> e.getValue() > 1);\n      if (multi) {\n        throw new RuntimeException(\"请确保微信开放平台配置 appId 的唯一性\");\n      }\n    }\n    WxOpenMultiServicesImpl services = new WxOpenMultiServicesImpl();\n\n    Set<Map.Entry<String, WxOpenSingleProperties>> entries = appsMap.entrySet();\n    for (Map.Entry<String, WxOpenSingleProperties> entry : entries) {\n      String tenantId = entry.getKey();\n      WxOpenSingleProperties wxOpenSingleProperties = entry.getValue();\n      WxOpenInMemoryConfigStorage storage = this.wxOpenConfigStorage(wxOpenMultiProperties);\n      this.configApp(storage, wxOpenSingleProperties);\n      this.configHttp(storage, wxOpenMultiProperties.getConfigStorage());\n      WxOpenService wxOpenService = this.wxOpenService(storage, wxOpenMultiProperties);\n      services.addWxOpenService(tenantId, wxOpenService);\n    }\n    return services;\n  }\n\n  /**","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/binarywang/WxJava/blob/1c43293a3c2c9d7e91304b6d037fb017f680d0c6/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/configuration/services/AbstractWxOpenConfiguration.java#L32-L68","documentation":"Thrown by the Spring Boot multi-account Open Platform (开放平台) starter when two tenants resolve to the same component appId, to avoid Redis token cache collisions. Unlike the other starters it uses a string placeholder \"__NULL_APP_ID__\" for null appIds (instead of the integer 0), so multiple null-appId entries also collide.","triggerScenarios":"Two entries under `wx.open.apps` with the same `appId`, or two entries both with a null appId (both mapped to \"__NULL_APP_ID__\").","commonSituations":"Reusing one component appId across tenant blocks; partial config where component appId was left blank on several entries.","solutions":["Ensure each `wx.open.apps.*.appId` (componentAppId) is unique and non-blank.","Restart the application."],"exampleFix":"# before\nwx.open.apps:\n  a: { appId: wxopen001 }\n  b: { appId: wxopen001 }\n# after\nwx.open.apps:\n  a: { appId: wxopen001 }\n  b: { appId: wxopen002 }","handlingStrategy":"validation","validationCode":"// Spring Boot Open multi: pre-flight component appId uniqueness\nSet<String> seen = new HashSet<>();\nfor (WxOpenSingleProperties p : appsMap.values()) {\n    String id = p.getAppId();\n    if (id == null || !seen.add(id)) {\n        throw new IllegalStateException(\"Duplicate/null open component appId\");\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["One componentAppId per tenant.","Fill all appIds; the null placeholder collides too.","Validate in CI before deploy."],"tags":["config","multi-tenant","open","spring-boot","startup"],"backgroundTag":null,"analyzedSha":"1c43293a3c2c9d7e91304b6d037fb017f680d0c6","analyzedAt":"2026-08-14T02:29:11.060Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}