apache/pulsar · error · IllegalArgumentException

Split service unit should be split into 2 service units.

Error message

Split service unit should be split into 2 service units.

What it means

The Split record models a topic-bundle split into exactly two child service units. Its compact constructor validates that splitServiceUnitToDestBroker is non-null with exactly 2 entries and throws IllegalArgumentException otherwise. It exists so callers cannot construct a malformed split plan.

Source

Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/models/Split.java:34

 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.pulsar.broker.loadbalance.extensions.models;

import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * Defines the information required for a service unit split(e.g. bundle split).
 */
public record Split(
        String serviceUnit, String sourceBroker, Map<String, Optional<String>> splitServiceUnitToDestBroker) {

    public Split {
        Objects.requireNonNull(serviceUnit);
        if (splitServiceUnitToDestBroker == null || splitServiceUnitToDestBroker.size() != 2) {
            throw new IllegalArgumentException("Split service unit should be split into 2 service units.");
        }
    }
}

View on GitHub (pinned to 820761864e)

Solutions

  1. Compute exactly two child service units from the bundle's split point and map each to a destination broker before constructing Split.
  2. Use the standard split logic (e.g. TopicRanges/HierarchyTopicPolicies bundle split) so the two halves are always produced.
  3. Validate the destinations map size before constructing the record.
  4. Fix tests to supply a two-entry map.

Example fix

// before
new Split(bundle, src, Map.of(firstHalf, destA)); // 1 entry -> throws
// after
new Split(bundle, src, Map.of(firstHalf, destA, secondHalf, destB)); // exactly 2 entries
Defensive patterns

Strategy: validation

Validate before calling

if (dests == null || dests.size() != 2) {
    throw new IllegalArgumentException("split must yield exactly 2 units, got "
        + (dests == null ? 0 : dests.size()));
}
Split split = new Split(serviceUnit, sourceBroker, dests);

Type guard

boolean isValidSplit(Split s) {
    return s != null && s.splitServiceUnitToDestBroker() != null
        && s.splitServiceUnitToDestBroker().size() == 2;
}

Try / catch

try {
    Split split = new Split(serviceUnit, sourceBroker, dests);
} catch (IllegalArgumentException e) {
    log.error("Bad split plan: {}", e.getMessage());
    // skip/redo split computation
}

Prevention

When it happens

Trigger: Calling new Split(serviceUnit, sourceBroker, destinations) with a null map, or a map with 0, 1, or 3+ entries (split points must yield exactly two halves).

Common situations: Custom load-manager code computing split destinations produced one or more than two children, a migration/assignment tool wrote wrong data, or a unit test built a Split with a single destination.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/33140845f1420d05. Report an issue: GitHub.