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

  1. Upgrade jedis to the version matching your Redis Enterprise server's push schema.
  2. Log and report the raw content included in the error to correlate with server version.
  3. Verify the connection is RESP3-capable (protocol=3) where maintenance pushes require it.
  4. 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

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.

Related errors


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)