redis/jedis · error · MalformedMaintenanceEventException

Malformed MIGRATING push

Error message

Malformed MIGRATING push: <content>

What it means

MaintenancePushCodec.migrating() validates MIGRATING push messages ( Redis Enterprise slot migration notifications): payload must be [MIGRATING, Long seq, Long time_s, byte[] shards]. Anything shorter or type-mismatched throws a malformed exception including the raw content.

Solutions

  1. Align jedis version with the Redis Enterprise/Redis version emitting the pushes.
  2. Capture the content in the error message and verify against the server's push spec; report a mismatch.
  3. Test with a direct connection (no proxy) to rule out payload mutation.
  4. Retry the connection after the maintenance window completes.
Defensive patterns

Strategy: validation

Validate before calling

static boolean isValidMigrating(List<Object> c) {
  return c.size() >= 4 && c.get(1) instanceof Long && c.get(2) instanceof Long
      && c.get(3) instanceof byte[];
}

Try / catch

try {
  handle(codec.decode(push));
} catch (JedisException e) {
  log.warn("Dropping malformed MIGRATING push: {}", e.getMessage());
}

Prevention

When it happens

Trigger: A MIGRATING push arrives with <4 elements, non-Long seq/time_s, or a shards field that is not a byte[] — typically a server/client schema mismatch or corrupted push stream.

Common situations: Redis Enterprise cluster performing slot migrations while client and server versions disagree on push format; proxies altering RESP arrays; mixed-version clusters during rolling upgrades.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


AI-assisted analysis of redis/jedis@6dac31d4c2 (2026-09-08). Data as JSON: /api/errors/8924618e3805509e. Report an issue: GitHub.

Appendix: source

Thrown at src/main/java/redis/clients/jedis/MaintenancePushCodec.java:81

  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)
        || !(c.get(3) instanceof byte[])) {
      throw malformed("FAILING_OVER", c);
    }
    return new FailingOverEvent((Long) c.get(1), (Long) c.get(2), shardIds(c, 3));
  }

  private static MaintenanceEvent migrated(List<Object> c) { // [MIGRATED, seq, shards]
    if (c.size() < 3 || !(c.get(1) instanceof Long) || !(c.get(2) instanceof byte[])) {
      throw malformed("MIGRATED", c);
    }
    return new MigratedEvent((Long) c.get(1), shardIds(c, 2));

View on GitHub (pinned to 6dac31d4c2)