apache/pulsar · error · IllegalArgumentException

Segment not found: ${segmentId}

Error message

Segment not found: ${segmentId}

What it means

SegmentLayout.splitSegment applies a split to a segment identified by segmentId in the immutable layout map. If no segment with that id exists, an IllegalArgumentException is thrown, because splitting an unknown segment would corrupt the hash-range layout.

Source

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

            }
            toVisit.addAll(segment.parentIds());
        }
        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],

View on GitHub (pinned to 820761864e)

Solutions

  1. Refresh to the latest SegmentLayout (e.g. via updated()) and re-resolve segment ids before splitting
  2. Validate segmentId exists via layout.allSegments.containsKey(id) (or equivalent) before calling
  3. Ensure split/merge coordination uses ids from the same layout epoch

Example fix

// before
layout.splitSegment(childIdFromOldSnapshot, now);
// after
SegmentLayout latest = updated();
if (latest.allSegments.containsKey(id)) { latest.splitSegment(id, now); }
Defensive patterns

Strategy: validation

Validate before calling

if (!layout.allSegments.containsKey(segmentId)) {
    throw new IllegalArgumentException("Unknown segmentId " + segmentId + "; valid: " + layout.allSegments.keySet());
}

Type guard

boolean segmentExists(SegmentLayout layout, long segmentId) { return layout.allSegments.get(segmentId) != null; }

Try / catch

try {
    layout.splitSegment(id, now);
} catch (IllegalArgumentException e) {
    log.warn("Split rejected: {} — refresh layout and retry with a valid id", e.getMessage());
}

Prevention

When it happens

Trigger: Calling splitSegment with an id that was never created, an id from a different/older layout snapshot (stale epoch), or an already-removed (merged) segment id.

Common situations: Concurrent split/merge operations where a caller holds ids from a pre-merge layout; replaying recorded segment ids after layout replacement; test scaffolding using made-up ids.

Related errors


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