apache/pulsar · error · IllegalArgumentException

Cannot split non-active segment

Error message

Cannot split non-active segment: ${segmentId}

What it means

SegmentLayout.splitSegment only allows splitting segments whose state is active. Passing the id of a non-active segment (e.g. a parent segment already split into children, or a sealed segment) throws an IllegalArgumentException to protect layout invariants — one hash range must map to exactly one active segment.

Solutions

  1. Select split targets only from segments where segment.isActive() is true in the current layout
  2. Refresh the layout snapshot and re-pick a candidate segment before splitting
  3. Track parent->children mapping so old parent ids are never reused as split targets

Example fix

// before
layout.splitSegment(parentId, now); // parent already split
// after
SegmentInfo target = layout.allSegments.values().stream()
    .filter(SegmentInfo::isActive).findFirst().orElseThrow();
layout.splitSegment(target.segmentId(), now);
Defensive patterns

Strategy: validation

Validate before calling

SegmentInfo s = layout.allSegments.get(segmentId);
if (s == null || !s.isActive()) {
    throw new IllegalArgumentException("segmentId " + segmentId + " is not an active segment");
}

Type guard

boolean isSplittable(SegmentLayout layout, long id) {
    SegmentInfo s = layout.allSegments.get(id);
    return s != null && s.isActive();
}

Try / catch

try {
    layout.splitSegment(id, now);
} catch (IllegalArgumentException e) {
    log.warn("Cannot split segment {}: not active — pick an active child instead", id);
}

Prevention

When it happens

Trigger: Calling splitSegment with the id of a parent segment that was previously split (children are the active ones), or any sealed/inactive segment id.

Common situations: Stale references to parent ids after a split; scheduling splits on segments selected from an outdated layout; tests exercising the non-active guard (testSplitNonActiveSegment).

Related errors


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

Appendix: source

Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/service/scalable/SegmentLayout.java:171

        return depth;
    }

    /**
     * Produce a new layout by splitting a segment at its midpoint.
     *
     * @param segmentId the active segment to split
     * @param nowMs     wall-clock millis used as the parent's seal time and the
     *                  children's create time. Caller passes a single value so
     *                  CAS retries and follow-up reads agree.
     * @return a new SegmentLayout with the split applied
     */
    public SegmentLayout splitSegment(long segmentId, long nowMs) {
        SegmentInfo segment = allSegments.get(segmentId);
        if (segment == null) {
            throw new IllegalArgumentException("Segment not found: " + segmentId);
        }
        if (!segment.isActive()) {
            throw new IllegalArgumentException("Cannot split non-active segment: " + segmentId);
        }

        HashRange[] splitRanges = segment.hashRange().split();
        long newEpoch = epoch + 1;
        long childId1 = nextSegmentId;
        long childId2 = nextSegmentId + 1;

        // PIP-486: a split divides the parent's entry-buckets between its children — N/2 each (at least
        // 1) — so the topic's total stays ≈ the budget as it fans out into more, narrower segments.
        List<Integer> childEntryBucketSplits =
                EntryBucketSplits.equalWidth(Math.max(1, segment.bucketCount() / 2));
        SegmentInfo sealedParent = segment.sealed(newEpoch, nowMs, List.of(childId1, childId2));
        SegmentInfo child1 = SegmentInfo.active(childId1, splitRanges[0],
                List.of(segmentId), newEpoch, nowMs).withEntryBucketSplits(childEntryBucketSplits);
        SegmentInfo child2 = SegmentInfo.active(childId2, splitRanges[1],
                List.of(segmentId), newEpoch, nowMs).withEntryBucketSplits(childEntryBucketSplits);

        Map<Long, SegmentInfo> newSegments = new LinkedHashMap<>(allSegments);

View on GitHub (pinned to 820761864e)