{"record":{"id":"b23c1786b17d27c6","repo":"dromara/Sa-Token","slug":"alone-redis-cluster-nodes","errorCode":null,"errorMessage":"Alone-Redis 集群模式需要配置 cluster.nodes","messagePattern":"Alone-Redis 集群模式需要配置 cluster\\.nodes","errorType":"validation","errorClass":"SaTokenException","httpStatus":null,"severity":"critical","filePath":"sa-token-plugin/sa-token-alone-redis-by-spring-boot4/src/main/java/cn/dev33/satoken/dao/alone/SaAloneRedisInject.java","lineNumber":107,"sourceCode":"\t\t\tif (pattern.equals(\"single\")) {\n\t\t\t\t// 单体模式\n\t\t\t\tRedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration();\n\t\t\t\tredisConfig.setHostName(cfg.getHost());\n\t\t\t\tredisConfig.setPort(cfg.getPort());\n\t\t\t\tredisConfig.setDatabase(cfg.getDatabase());\n\t\t\t\tredisConfig.setPassword(RedisPassword.of(cfg.getPassword()));\n\t\t\t\tredisConfig.setUsername(cfg.getUsername());\n\t\t\t\tredisAloneConfig = redisConfig;\n\n\t\t\t} else if (pattern.equals(\"cluster\")){\n\t\t\t\t// 普通集群模式\n\t\t\t\tRedisClusterConfiguration redisClusterConfig = new RedisClusterConfiguration();\n\t\t\t\tredisClusterConfig.setUsername(cfg.getUsername());\n\t\t\t\tredisClusterConfig.setPassword(RedisPassword.of(cfg.getPassword()));\n\n\t\t\t\tDataRedisProperties.Cluster cluster = cfg.getCluster();\n\t\t\t\tif (cluster == null || cluster.getNodes() == null) {\n\t\t\t\t\tthrow new SaTokenException(\"Alone-Redis 集群模式需要配置 cluster.nodes\");\n\t\t\t\t}\n\t\t\t\tList<RedisNode> serverList = cluster.getNodes().stream().map(node -> {\n\t\t\t\t\tString[] ipAndPort = node.split(\":\");\n\t\t\t\t\treturn new RedisNode(ipAndPort[0].trim(), Integer.parseInt(ipAndPort[1]));\n\t\t\t\t}).collect(Collectors.toList());\n\t\t\t\tredisClusterConfig.setClusterNodes(serverList);\n\t\t\t\tif (cluster.getMaxRedirects() != null) {\n\t\t\t\t\tredisClusterConfig.setMaxRedirects(cluster.getMaxRedirects());\n\t\t\t\t}\n\t\t\t\tredisAloneConfig = redisClusterConfig;\n\t\t\t} else if (pattern.equals(\"sentinel\")) {\n\t\t\t\t// 哨兵集群模式\n\t\t\t\tRedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration();\n\t\t\t\tredisSentinelConfiguration.setDatabase(cfg.getDatabase());\n\t\t\t\tredisSentinelConfiguration.setUsername(cfg.getUsername());\n\t\t\t\tredisSentinelConfiguration.setPassword(RedisPassword.of(cfg.getPassword()));\n\n\t\t\t\tDataRedisProperties.Sentinel sentinel = cfg.getSentinel();","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/dromara/Sa-Token/blob/ac2c7f6e94a78573cf0bcb932dd8b04e68fad189/sa-token-plugin/sa-token-alone-redis-by-spring-boot4/src/main/java/cn/dev33/satoken/dao/alone/SaAloneRedisInject.java#L89-L125","documentation":"SaTokenException thrown by SaAloneRedisInject (spring-boot4 variant of sa-token-alone-redis) when sa-token.alone-redis.pattern=cluster is configured but the nested cluster.nodes list is absent. The injector builds a RedisClusterConfiguration and cannot proceed without at least one 'host:port' node entry.","triggerScenarios":"Setting sa-token.alone-redis.pattern=cluster in application.yml without a sa-token.alone-redis.cluster.nodes: [host:port, ...] block (or misspelling 'nodes' / nesting it under the wrong level so Spring Boot's DataRedisProperties.Cluster stays null).","commonSituations":"Migrating a standalone alone-redis config to a cluster by only changing pattern; yaml indentation errors putting cluster.nodes under sa-token instead of sa-token.alone-redis.cluster.nodes; copying config from the non-boot4 plugin whose property paths differ.","solutions":["Add the nodes list, e.g. sa-token.alone-redis.cluster.nodes: [\"10.0.0.1:6379\", \"10.0.0.2:6379\"].","Check yaml indentation so cluster: sits under sa-token.alone-redis, and nodes: is a list of host:port strings.","Optionally set cluster.max-redirects (e.g. 3) as recommended for Redis Cluster.","Verify pattern value is exactly 'cluster' (not 'redis-cluster') so this branch is the one requiring the config."],"exampleFix":"# before\nsa-token:\n  alone-redis:\n    pattern: cluster\n\n# after\nsa-token:\n  alone-redis:\n    pattern: cluster\n    cluster:\n      max-redirects: 3\n      nodes:\n        - 10.0.0.1:6379\n        - 10.0.0.2:6379\n        - 10.0.0.3:6379","handlingStrategy":"validation","validationCode":"// at startup / config check, before the injector runs\nif (\"cluster\".equals(props.getPattern()) && (props.getCluster() == null || props.getCluster().getNodes() == null || props.getCluster().getNodes().isEmpty())) {\n    throw new IllegalStateException(\"alone-redis cluster mode requires cluster.nodes\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat alone-redis config as code: validate pattern + required sub-block in a config-check test that runs in CI.","When changing pattern, re-read the injector's branch for that mode and add every block it dereferences."],"tags":["alone-redis","redis-cluster","spring-boot4","configuration"],"backgroundTag":null,"analyzedSha":"ac2c7f6e94a78573cf0bcb932dd8b04e68fad189","analyzedAt":"2026-08-14T14:36:10.271Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}