{"record":{"id":"d5293eda1932dbf9","repo":"binarywang/WxJava","slug":"appid-d5293e","errorCode":null,"errorMessage":"请确保微信视频号配置 appId 的唯一性","messagePattern":"请确保微信视频号配置 appId 的唯一性","errorType":"validation","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/configuration/services/AbstractWxChannelConfiguration.java","lineNumber":51,"sourceCode":"    Map<String, WxChannelSingleProperties> appsMap = wxChannelMultiProperties.getApps();\n    if (appsMap == null || appsMap.isEmpty()) {\n      log.warn(\"微信视频号应用参数未配置，通过 WxChannelMultiServices#getWxChannelService(\\\"tenantId\\\")获取实例将返回空\");\n      return new WxChannelMultiServicesImpl();\n    }\n    /**\n     * 校验 appId 是否唯一，避免使用 redis 缓存 token、ticket 时错乱。\n     *\n     * 查看 {@link me.chanjar.weixin.channel.config.impl.WxChannelRedisConfigImpl#setAppid(String)}\n     */\n    Collection<WxChannelSingleProperties> apps = appsMap.values();\n    if (apps.size() > 1) {\n      // 校验 appId 是否唯一\n      boolean multi = apps.stream()\n        // 没有 appId，如果不判断是否为空，这里会报 NPE 异常\n        .collect(Collectors.groupingBy(c -> c.getAppId() == null ? 0 : c.getAppId(), Collectors.counting()))\n        .entrySet().stream().anyMatch(e -> e.getValue() > 1);\n      if (multi) {\n        throw new RuntimeException(\"请确保微信视频号配置 appId 的唯一性\");\n      }\n    }\n    WxChannelMultiServicesImpl services = new WxChannelMultiServicesImpl();\n\n    Set<Map.Entry<String, WxChannelSingleProperties>> entries = appsMap.entrySet();\n    for (Map.Entry<String, WxChannelSingleProperties> entry : entries) {\n      String tenantId = entry.getKey();\n      WxChannelSingleProperties wxChannelSingleProperties = entry.getValue();\n      WxChannelDefaultConfigImpl storage = this.wxChannelConfigStorage(wxChannelMultiProperties);\n      this.configApp(storage, wxChannelSingleProperties);\n      this.configHttp(storage, wxChannelMultiProperties.getConfigStorage());\n      WxChannelService wxChannelService = this.wxChannelService(storage, wxChannelMultiProperties);\n      services.addWxChannelService(tenantId, wxChannelService);\n    }\n    return services;\n  }\n\n  /**","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/binarywang/WxJava/blob/1c43293a3c2c9d7e91304b6d037fb017f680d0c6/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/configuration/services/AbstractWxChannelConfiguration.java#L33-L69","documentation":"Thrown by the Spring Boot multi-account Channel (视频号) starter when two tenant entries resolve to the same appId, to prevent Redis-based token/ticket cache key collisions (see WxChannelRedisConfigImpl#setAppid). It is a RuntimeException that aborts bean creation at startup.","triggerScenarios":"Two entries under `wx.channel.apps` (or `wxjava.channel.apps`) with the same `appId`; two entries with null appId (both collapse to grouping key `0`).","commonSituations":"Duplicated tenant YAML blocks; one appId shared by mistake; migrating from single- to multi-account and leaving the same appId on two keys.","solutions":["List all `wx.channel.apps.*.appId` values and deduplicate.","Make each tenant's appId unique; remove blanks.","Restart; the check only passes when no grouping key has count > 1."],"exampleFix":"# before\nwx.channel.apps:\n  tenant-a: { appId: wxch001 }\n  tenant-b: { appId: wxch001 }\n# after\nwx.channel.apps:\n  tenant-a: { appId: wxch001 }\n  tenant-b: { appId: wxch002 }","handlingStrategy":"validation","validationCode":"// Spring Boot Channel multi: pre-flight appId uniqueness\nSet<String> seen = new HashSet<>();\nfor (WxChannelSingleProperties p : appsMap.values()) {\n    if (p.getAppId() == null || !seen.add(p.getAppId())) {\n        throw new IllegalStateException(\"Duplicate/null channel appId\");\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use distinct appIds per tenant.","Add a CI config validator that fails on duplicate appIds.","Keep Redis namespace planning aligned with appId assignment."],"tags":["config","multi-tenant","channel","spring-boot","startup"],"backgroundTag":null,"analyzedSha":"1c43293a3c2c9d7e91304b6d037fb017f680d0c6","analyzedAt":"2026-08-14T02:29:11.060Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}