{"record":{"id":"7ea566bd8d6eccc1","repo":"redisson/redisson","slug":"s-pushx-only-allows-one-value","errorCode":null,"errorMessage":"%s PUSHX only allows one value!","messagePattern":"(.+?) PUSHX only allows one value!","errorType":"exception","errorClass":"InvalidDataAccessApiUsageException","httpStatus":null,"severity":"error","filePath":"redisson-spring/redisson-spring-data/redisson-spring-data-20/src/main/java/org/redisson/spring/data/connection/RedissonReactiveListCommands.java","lineNumber":68,"sourceCode":"\n    private static final RedisStrictCommand<Long> RPUSH = new RedisStrictCommand<Long>(\"RPUSH\");\n    private static final RedisStrictCommand<Long> LPUSH = new RedisStrictCommand<Long>(\"LPUSH\");\n    private static final RedisStrictCommand<Long> RPUSHX = new RedisStrictCommand<Long>(\"RPUSHX\");\n    private static final RedisStrictCommand<Long> LPUSHX = new RedisStrictCommand<Long>(\"LPUSHX\");\n\n    RedissonReactiveListCommands(CommandReactiveExecutor executorService) {\n        super(executorService);\n    }\n\n    @Override\n    public Flux<NumericResponse<PushCommand, Long>> push(Publisher<PushCommand> commands) {\n        return execute(commands, command -> {\n\n            Assert.notNull(command.getKey(), \"Key must not be null!\");\n            Assert.notEmpty(command.getValues(), \"Values must not be null or empty!\");\n\n            if (!command.getUpsert() && command.getValues().size() > 1) {\n                throw new InvalidDataAccessApiUsageException(\n                        String.format(\"%s PUSHX only allows one value!\", command.getDirection()));\n            }\n\n            RedisStrictCommand<Long> redisCommand;\n            \n            List<Object> params = new ArrayList<Object>();\n            params.add(toByteArray(command.getKey()));\n            params.addAll(command.getValues().stream().map(v -> toByteArray(v)).collect(Collectors.toList()));\n\n            if (ObjectUtils.nullSafeEquals(Direction.RIGHT, command.getDirection())) {\n                if (command.getUpsert()) {\n                    redisCommand = RPUSH;\n                } else {\n                    redisCommand = RPUSHX;\n                }\n            } else {\n                if (command.getUpsert()) {\n                    redisCommand = LPUSH;","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/redisson/redisson/blob/91188987c2a9023e7fedabff758681ad9b107f25/redisson-spring/redisson-spring-data/redisson-spring-data-20/src/main/java/org/redisson/spring/data/connection/RedissonReactiveListCommands.java#L50-L86","documentation":"RedissonReactiveListCommands.push(PushCommand) throws InvalidDataAccessApiUsageException('<dir> PUSHX only allows one value!') when the command has upsert=false (meaning LPUSHX/RPUSHX semantics) but carries more than one value. Redis' PUSHX commands accept exactly one value, unlike plain LPUSH/RPUSH.","triggerScenarios":"Building a PushCommand via RedisListCommands.PushCommand.push(key, value, direction).untilAbsent(true) — or setting upsert(false) — with a values list of length > 1, then executing it through the reactive list commands.","commonSituations":"Reusing a generic 'push many values' helper for both upsert and non-upsert paths; switching a working multi-value LPUSH to untilAbsent() without splitting values.","solutions":["Split multiple values into one PushX command per value, each with a single-element values list.","If the key is guaranteed to exist, drop untilAbsent()/upsert(false) so plain LPUSH/RPUSH handles multiple values.","Validate values.size() <= 1 before submitting an upsert=false command."],"exampleFix":"// before\nPushCommand cmd = PushCommand.push(key, Direction.RIGHT, v1, v2).untilAbsent(true);\nlistCommands.push(Flux.just(cmd)); // throws: RIGHT PUSHX only allows one value!\n\n// after\nFlux<PushCommand> cmds = Flux.just(\n    PushCommand.push(key, Direction.RIGHT, v1).untilAbsent(true),\n    PushCommand.push(key, Direction.RIGHT, v2).untilAbsent(true));\nlistCommands.push(cmds);","handlingStrategy":"validation","validationCode":"// split multi-value upsert=false pushes into single-value commands\nList<PushCommand> safe = new ArrayList<>();\nif (!cmd.getUpsert() && cmd.getValues().size() > 1) {\n    for (ByteBuffer v : cmd.getValues()) {\n        safe.add(PushCommand.push(cmd.getKey(), cmd.getDirection(), v).untilAbsent(true));\n    }\n} else {\n    safe.add(cmd);\n}","typeGuard":"boolean isValidPushX(PushCommand cmd) {\n    return cmd.getUpsert() || cmd.getValues().size() <= 1;\n}","tryCatchPattern":null,"preventionTips":["Remember PUSHX (LPUSHX/RPUSHX) is single-value in Redis; only plain LPUSH/RPUSH accept many","Encapsulate push-command construction in one helper that enforces the PUSHX rule"],"tags":["redis","redisson","reactive","list-commands","validation"],"backgroundTag":null,"analyzedSha":"91188987c2a9023e7fedabff758681ad9b107f25","analyzedAt":"2026-08-14T11:39:42.619Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}