apache/pulsar · warning · RestException
Subscription not found on segment: ${subscription}
Error message
Subscription not found on segment: ${subscription} What it means
HTTP 404 thrown by the segment subscription-backlog endpoint when the segment topic is loaded but has no subscription with the requested name. Backlog is per-cursor, so without the named subscription there is nothing to report.
Source
Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Segments.java:326
@QueryParam("authoritative") @DefaultValue("false") boolean authoritative) {
validateNamespaceName(tenant, namespace);
TopicName segmentTopic = segmentTopicName(tenant, namespace, encodedTopic, descriptor);
validateSuperUserAccessAsync()
.thenCompose(__ -> validateTopicOwnershipAsync(segmentTopic, authoritative))
.thenCompose(__ -> pulsar().getBrokerService().getTopicIfExists(segmentTopic.toString()))
.thenAccept(optTopic -> {
if (optTopic.isEmpty()) {
// No topic loaded → no subscription cursor → no backlog. Returning
// 0 here would be wrong (caller might mark the segment drained on
// a topic that simply hasn't loaded yet); a 404 forces the caller
// to retry, which matches our drain-poll contract.
throw new RestException(Response.Status.NOT_FOUND,
"Segment topic not loaded: " + segmentTopic);
}
var sub = optTopic.get().getSubscription(subscription);
if (sub == null) {
throw new RestException(Response.Status.NOT_FOUND,
"Subscription not found on segment: " + subscription);
}
asyncResponse.resume(sub.getNumberOfEntriesInBacklog(false));
})
.exceptionally(ex -> {
log.error().attr("clientAppId", clientAppId()).attr("segment", segmentTopic)
.exception(ex).log("Failed to get segment subscription backlog");
resumeAsyncResponseExceptionally(asyncResponse, ex);
return null;
});
}
@POST
@Path("/{tenant}/{namespace}/{topic}/{descriptor}/subscription/{subscription}/seek")
@Operation(summary = "Reset the segment topic's subscription cursor to the given timestamp."
+ " Super-user only.")
@ApiResponses(value = {
@ApiResponse(responseCode = "204", description = "Cursor reset successfully"),View on GitHub (pinned to 820761864e)
Solutions
- Use the exact subscription name the consumer used (check consumer config/logs).
- Ensure the consuming client has connected to every segment at least once so the subscription cursor exists on each segment.
- Create the subscription explicitly (admin API) on the segment before polling backlog, or treat 404 as 'not draining yet'.
Example fix
// before await getBacklog(seg, 'my-sub'); // 404: never created on this segment // after await admin.topics().createSubscriptionAsync(seg, 'my-sub', MessageId.earliest); const backlog = await getBacklog(seg, 'my-sub');
Defensive patterns
Strategy: validation
Validate before calling
const subs = await admin.topics().getSubscriptionsAsync(segmentTopic);
if (!subs.includes(subscription)) throw new Error(`subscription '${subscription}' not created on ${segmentTopic}`); Type guard
const subscriptionExists = (subs, name) => Array.isArray(subs) && subs.includes(name);
Try / catch
try {
const backlog = await admin.scalableTopics().getSubscriptionBacklog(segmentTopic, sub);
} catch (e) {
if (e.status === 404 && /Subscription not found/.test(e.message)) return Infinity; // not draining
throw e;
} Prevention
- Use the exact subscription name configured on consumers; source it from shared config, not literals.
- Ensure the consumer connects to every segment before drain checks, or pre-create the subscription on all segments.
- Re-check subscriptions after consumer restarts — an unsubscribe removes cursors.
When it happens
Trigger: Polling GET backlog with a subscription name that no reader/consumer ever created on that segment — wrong subscription name, subscription not yet created on this segment (each segment has its own cursors), or the subscription was deleted/unsubscribed.
Common situations: Drain tooling assuming a subscription exists on every segment when readers only subscribed to some; typo'd subscription name; checking backlog before the consumer that creates the subscription first connected; subscribe-on-demand vs pre-created subscription mismatch.
Related errors
- Segment topic not loaded: ${segmentTopic}
- Segment topic not found: ${segmentTopic}
- Topic does not exist: ${persistentBase}
- Scalable topic not found: ${tn}
- Namespace does not exist
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/3b3c9e1ffa5ce09e.
Report an issue: GitHub.