{"record":{"id":"3b3c9e1ffa5ce09e","repo":"apache/pulsar","slug":"subscription-not-found-on-segment-subscription","errorCode":null,"errorMessage":"Subscription not found on segment: ${subscription}","messagePattern":"Subscription not found on segment: (.+?)","errorType":"http","errorClass":"RestException","httpStatus":404,"severity":"warning","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Segments.java","lineNumber":326,"sourceCode":"            @QueryParam(\"authoritative\") @DefaultValue(\"false\") boolean authoritative) {\n        validateNamespaceName(tenant, namespace);\n        TopicName segmentTopic = segmentTopicName(tenant, namespace, encodedTopic, descriptor);\n\n        validateSuperUserAccessAsync()\n                .thenCompose(__ -> validateTopicOwnershipAsync(segmentTopic, authoritative))\n                .thenCompose(__ -> pulsar().getBrokerService().getTopicIfExists(segmentTopic.toString()))\n                .thenAccept(optTopic -> {\n                    if (optTopic.isEmpty()) {\n                        // No topic loaded → no subscription cursor → no backlog. Returning\n                        // 0 here would be wrong (caller might mark the segment drained on\n                        // a topic that simply hasn't loaded yet); a 404 forces the caller\n                        // to retry, which matches our drain-poll contract.\n                        throw new RestException(Response.Status.NOT_FOUND,\n                                \"Segment topic not loaded: \" + segmentTopic);\n                    }\n                    var sub = optTopic.get().getSubscription(subscription);\n                    if (sub == null) {\n                        throw new RestException(Response.Status.NOT_FOUND,\n                                \"Subscription not found on segment: \" + subscription);\n                    }\n                    asyncResponse.resume(sub.getNumberOfEntriesInBacklog(false));\n                })\n                .exceptionally(ex -> {\n                    log.error().attr(\"clientAppId\", clientAppId()).attr(\"segment\", segmentTopic)\n                            .exception(ex).log(\"Failed to get segment subscription backlog\");\n                    resumeAsyncResponseExceptionally(asyncResponse, ex);\n                    return null;\n                });\n    }\n\n    @POST\n    @Path(\"/{tenant}/{namespace}/{topic}/{descriptor}/subscription/{subscription}/seek\")\n    @Operation(summary = \"Reset the segment topic's subscription cursor to the given timestamp.\"\n            + \" Super-user only.\")\n    @ApiResponses(value = {\n            @ApiResponse(responseCode = \"204\", description = \"Cursor reset successfully\"),","sourceCodeStart":308,"sourceCodeEnd":344,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Segments.java#L308-L344","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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'."],"exampleFix":"// before\nawait getBacklog(seg, 'my-sub'); // 404: never created on this segment\n// after\nawait admin.topics().createSubscriptionAsync(seg, 'my-sub', MessageId.earliest);\nconst backlog = await getBacklog(seg, 'my-sub');","handlingStrategy":"validation","validationCode":"const subs = await admin.topics().getSubscriptionsAsync(segmentTopic);\nif (!subs.includes(subscription)) throw new Error(`subscription '${subscription}' not created on ${segmentTopic}`);","typeGuard":"const subscriptionExists = (subs, name) => Array.isArray(subs) && subs.includes(name);","tryCatchPattern":"try {\n  const backlog = await admin.scalableTopics().getSubscriptionBacklog(segmentTopic, sub);\n} catch (e) {\n  if (e.status === 404 && /Subscription not found/.test(e.message)) return Infinity; // not draining\n  throw e;\n}","preventionTips":["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."],"tags":["rest","not-found","subscription","backlog","segment","http-404"],"backgroundTag":"subscription-not-found","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}