apache/pulsar · error · IllegalStateException
Synthetic layout missing segment_id= + partition + (N= + n
Error message
Synthetic layout missing segment_id= + partition + (N= + n + )
What it means
SegmentRouter.routeModN maps a message key hash onto an active segment via a sign-safe modulo of the hash by the number of segments (N). This IllegalStateException is thrown when the computed partition index does not match any currently active segment, meaning the in-memory synthetic layout has drifted from the expected 0..N-1 segment id range. The library throws it rather than silently dropping or misrouting the message.
Source
Thrown at pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/SegmentRouter.java:103
}
}
return true;
}
/**
* Mod-N routing over {@code segment_id}, matching v4 partitioned-topic routing
* ({@code signSafeMod(murmurHash3_32(key), N)}).
*/
private static long routeModN(String key, List<ActiveSegment> activeSegments) {
int hash32 = Murmur3_32Hash.getInstance().makeHash(key.getBytes(StandardCharsets.UTF_8));
int n = activeSegments.size();
int partition = signSafeMod(hash32, n);
for (var segment : activeSegments) {
if (segment.segmentId() == partition) {
return segment.segmentId();
}
}
throw new IllegalStateException(
"Synthetic layout missing segment_id=" + partition + " (N=" + n + ")");
}
private static int signSafeMod(int dividend, int divisor) {
int mod = dividend % divisor;
return mod < 0 ? mod + divisor : mod;
}
/**
* The raw 32-bit {@code Murmur3_32} hash of a key. Its two 16-bit halves drive the two independent
* rings — high half → segment routing ({@link #segmentHash}), low half → entry-bucketing
* ({@link #entryBucketHash}, PIP-486). Compute this <b>once</b> per key and split it, rather than
* hashing the key twice.
*
* <p>The <i>raw</i> (unmasked) hash is required so the high half is full-range: {@code makeHash}
* clears bit 31, which would confine the high half to {@code [0, 0x7FFF]}.
*/
static int murmur(byte[] keyBytes) {View on GitHub (pinned to 820761864e)
Solutions
- Rebuild or refresh the router's activeSegments list so it is contiguous and consistent with the value of n used for the modulo.
- Route under the same lock/consistency point that mutates activeSegments so segment removal cannot race with routing.
- Verify segment ids cover 0..N-1 at construction; fail fast or recompute n from activeSegments.size() instead of a stale count.
- Catch IllegalStateException in route() callers and retry against a refreshed router.
Example fix
// before
int n = configuredSegmentCount;
int partition = signSafeMod(hash32, n);
for (var segment : activeSegments) {
if (segment.segmentId() == partition) {
return segment.segmentId();
}
}
throw new IllegalStateException("Synthetic layout missing segment_id=" + partition + " (N=" + n + ")");
// after
var snapshot = new ArrayList<>(activeSegments); // consistent view, taken under lock
int n = snapshot.size();
int partition = signSafeMod(hash32, n);
return snapshot.stream()
.filter(s -> s.segmentId() == partition)
.findFirst()
.map(Segment::segmentId)
.orElseGet(() -> snapshot.get(Math.floorMod(partition, snapshot.size())).segmentId()); Defensive patterns
Strategy: validation
Validate before calling
if (activeSegments.size() != n || activeSegments.stream().mapToInt(Segment::segmentId).distinct().count() != n) {
throw new IllegalStateException("segment layout not contiguous for N=" + n);
} Try / catch
try { return router.route(key); } catch (IllegalStateException e) { router.refreshLayout(); return router.route(key); } Prevention
- Derive n from activeSegments.size() rather than a separate configured count
- Mutate activeSegments and route under the same lock or use an immutable snapshot reference
- Assert segment ids are exactly 0..N-1 when building the router
- Add a self-check at router construction that maps sample hashes to valid segments
When it happens
Trigger: Calling route() (which delegates to routeModN) when activeSegments does not contain a segment whose segmentId() equals signSafeMod(hash32, n) — e.g. segments were closed/removed, reordered, or the active segment list is sparse while n still counts all segments.
Common situations: A segment was closed or failed while routing continued with a stale count of active segments; concurrent layout changes (segment add/remove) racing with route; constructing the router with non-contiguous segment ids; rebalancing/reshuffling the synthetic layout mid-flight.
Related errors
- No active segments
- No segment covers hash + hash + for key: + key
- Concurrent modification
- Segment not found: ${segmentId}
- Cannot split non-active segment: ${segmentId}
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/b38065b2da14933e.
Report an issue: GitHub.