redis/jedis · error · MalformedMaintenanceEventException
Malformed MOVING push:
Error message
Malformed MOVING push:
What it means
MaintenancePushCodec.moving() parses RESP3 push notifications of type MOVING (sent during Redis Enterprise maintenance operations). If the payload list is too short or the seq/time fields are not Longs, a malformed exception is thrown describing the raw content. A null target is valid (means 'none' endpoint); a wrong-type or unparseable host:port target is also treated as malformed by parseHostPort.
Solutions
- Upgrade jedis to the version matching your Redis Enterprise server's push schema.
- Log and report the raw content included in the error to correlate with server version.
- Verify the connection is RESP3-capable (protocol=3) where maintenance pushes require it.
- Check for intermediary proxies mutating push payloads and bypass them if possible.
Example fix
// before JedisClientConfig cfg = ...; // protocol RESP2 // after defaultRedisClientBuilder.protocol(RedisProtocol.RESP3) // enable RESP3 pushes
Defensive patterns
Strategy: validation
Validate before calling
// require RESP3 and matching client version before maintenance-aware features
if (config.getProtocol() != RedisProtocol.RESP3) {
throw new IllegalArgumentException("Maintenance pushes require RESP3");
} Type guard
// validate expected MOVING shape: [MOVING, Long, Long, target]
static boolean isValidMoving(List<Object> c) {
return c.size() >= 4 && c.get(1) instanceof Long && c.get(2) instanceof Long
&& (c.get(3) == null || c.get(3) instanceof byte[]);
} Try / catch
try {
handle(codec.decode(push));
} catch (JedisException e) { // malformed push
log.warn("Unparseable maintenance push, ignoring: {}", e.getMessage());
} Prevention
- Run a jedis version matching your Redis Enterprise server version
- Use RESP3 protocol where maintenance pushes are expected
- Avoid custom proxies in the push path
- Pin client/server versions and test maintenance events in staging
When it happens
Trigger: A MOVING push arrives with fewer than 4 elements, non-Long seq/time_s, or an unparseable/non-byte[] host:port — e.g. protocol incompatibility between the server's push format and the client codec, or corrupted/interleaved push data.
Common situations: Connecting a mismatched jedis version to a Redis Enterprise cluster using a newer/older maintenance push schema; custom proxies rewriting push payloads; RESP2 clients receiving push frames unexpectedly.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Malformed MIGRATING push
- Malformed FAILING_OVER push
- Malformed MIGRATED push
- Malformed FAILED_OVER push
- Ignoring malformed maintenance push
AI-assisted analysis of redis/jedis@6dac31d4c2 (2026-09-08).
Data as JSON: /api/errors/3297fec83bc0e3a5.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/redis/clients/jedis/MaintenancePushCodec.java:70
return null;
}
}
}
/**
* Builds the domain event for an already-resolved push type.
* @throws MalformedMaintenanceEventException if the frame's fields are malformed (missing or
* wrong-typed seq/time/shards, or a MOVING with a missing or unparseable target — a
* null target is valid and denotes the {@code none} endpoint type)
*/
static MaintenanceEvent build(PushType type, PushMessage msg) {
return type.decoder.apply(msg.getContent());
}
private static MaintenanceEvent moving(List<Object> c) { // [MOVING, seq, time_s, host:port |
// null]
if (c.size() < 4 || !(c.get(1) instanceof Long) || !(c.get(2) instanceof Long)) {
throw malformed("MOVING", c);
}
// Explicit RESP3 null target => 'none' endpoint type (no remap). A byte[] target is parsed;
// anything else (wrong type, unparseable) is malformed.
HostAndPort target = c.get(3) == null ? null : parseHostPort(c, 3);
return new MovingEvent((Long) c.get(1), (Long) c.get(2), target);
}
private static MaintenanceEvent migrating(List<Object> c) { // [MIGRATING, seq, time_s, shards]
if (c.size() < 4 || !(c.get(1) instanceof Long) || !(c.get(2) instanceof Long)
|| !(c.get(3) instanceof byte[])) {
throw malformed("MIGRATING", c);
}
return new MigratingEvent((Long) c.get(1), (Long) c.get(2), shardIds(c, 3));
}
private static MaintenanceEvent failingOver(List<Object> c) { // [FAILING_OVER, seq, time_s,
// shards]
if (c.size() < 4 || !(c.get(1) instanceof Long) || !(c.get(2) instanceof Long)View on GitHub (pinned to 6dac31d4c2)