apache/pulsar · error · IllegalArgumentException

Cannot split non-active segment: ${segmentId}

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.

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)

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.