apache/pulsar · error · PulsarClientException
(wraps v4 producer send failure message)
Error message
(wraps v4 producer send failure message)
What it means
During sendInternal, if the underlying v4 (per-segment) producer's send() fails with an error that is not a 'segment gone' condition, the failure is immediately rethrown as a PulsarClientException carrying the original message. Only segment-gone errors are retried; everything else propagates on the first attempt.
Source
Thrown at pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/ScalableTopicProducer.java:219
for (int attempt = 0; attempt < SEND_RETRY_MAX_ATTEMPTS; attempt++) {
long segmentId = routeMessage(key);
try {
var producer = getOrCreateSegmentProducer(segmentId);
var v4MsgId = buildV4Message(producer, key, value, properties,
eventTime, sequenceId, deliverAfter, deliverAt, replicationClusters, txn)
.send();
return new MessageIdV5(v4MsgId, segmentId);
} catch (PulsarClientException e) {
// Thrown while (re)creating the per-segment producer — already a V5 exception
// (it may wrap a v4 TopicTerminated/AlreadyClosed cause).
if (!isSegmentGoneError(e)) {
throw e;
}
lastError = e;
} catch (org.apache.pulsar.client.api.PulsarClientException e) {
// Thrown by the v4 producer's send().
if (!isSegmentGoneError(e)) {
throw new PulsarClientException(e.getMessage(), e);
}
lastError = new PulsarClientException(e.getMessage(), e);
}
// The target segment is gone: sealed by a split/merge, or terminated by a
// regular-to-scalable migration. Drop the stale per-segment producer and wait
// for the DAG watch to deliver the new layout; routeMessage on the next attempt
// lands on an active child.
log.info().attr("segmentId", segmentId).attr("attempt", attempt + 1)
.log("Target segment gone, waiting for layout update");
segmentProducers.remove(segmentId);
try {
Thread.sleep(Math.min(100L * (attempt + 1), SEND_RETRY_MAX_BACKOFF_MS));
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new PulsarClientException("Interrupted while waiting for layout update", ie);
}
}
throw lastError != null ? lastErrorView on GitHub (pinned to 820761864e)
Solutions
- Inspect the message/cause: for broker unavailability check serviceUrl and broker health before retrying.
- If the topic was actually migrated/sealed but isSegmentGoneError() misclassified it, upgrade the library or widen the segment-gone detection pattern.
- Renew credentials if the cause is authentication/authorization.
- Wrap send with application-level retry with backoff for transient broker errors, since sendInternal does not retry non-segment-gone errors.
Example fix
// before
producer.send(msg); // throws on transient broker error
// after
try {
producer.send(msg);
} catch (PulsarClientException e) {
if (isRetryable(e)) { retryWithBackoff(msg); } else { throw e; }
} Defensive patterns
Strategy: retry
Validate before calling
// before sending
if (producer == null || topicState == TERMINATED) { throw new IllegalStateException("topic unavailable"); } Try / catch
try {
producer.send(msg);
} catch (PulsarClientException e) {
if (isTransient(e)) { retryWithBackoff(msg); } else { throw e; }
} Prevention
- Implement application-level retry with backoff for transient broker errors
- Keep broker connectivity and credentials valid (token renewal)
- Upgrade the library if segment-gone detection misclassifies migration errors
When it happens
Trigger: Calling send()/MessageBuilderV5 send where the v4 segment producer fails for reasons like topic terminated, producer fenced, authentication failure, or broker unavailable, and the error does not match the isSegmentGoneError() heuristic.
Common situations: Broker restarts or network partitions mid-send; topic deleted while a scalable producer is attached; auth token expiry; producing after a regular-to-scalable migration misdetected as a normal failure.
Related errors
- Error creating client for HealthChecker
- Interrupted while waiting for layout update
- (wraps v4 producer error message)
- Cannot start the service once it was stopped
- webServicePort/webServicePortTls or http/https bindAddresses
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/8df99480ce87e9a4.
Report an issue: GitHub.