{"record":{"id":"29b958b1fbf58844","repo":"apache/pulsar","slug":"segment-topic-not-found-segmenttopic","errorCode":null,"errorMessage":"Segment topic not found: ${segmentTopic}","messagePattern":"Segment topic not found: (.+?)","errorType":"http","errorClass":"RestException","httpStatus":404,"severity":"error","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Segments.java","lineNumber":163,"sourceCode":"            @Parameter(description = \"Specify the tenant\", required = true)\n            @PathParam(\"tenant\") String tenant,\n            @Parameter(description = \"Specify the namespace\", required = true)\n            @PathParam(\"namespace\") String namespace,\n            @Parameter(description = \"Specify the parent topic name\", required = true)\n            @PathParam(\"topic\") @Encoded String encodedTopic,\n            @Parameter(description = \"Segment descriptor (e.g. 0000-7fff-1)\", required = true)\n            @PathParam(\"descriptor\") String descriptor,\n            @Parameter(description = \"Whether leader broker redirected this call to this broker.\")\n            @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                .thenCompose(optTopic -> {\n                    if (optTopic.isEmpty()) {\n                        throw new RestException(Response.Status.NOT_FOUND,\n                                \"Segment topic not found: \" + segmentTopic);\n                    }\n                    if (optTopic.get() instanceof PersistentTopic pt) {\n                        return pt.terminate().thenApply(__ -> null);\n                    }\n                    throw new RestException(Response.Status.BAD_REQUEST,\n                            \"Cannot terminate non-persistent topic: \" + segmentTopic);\n                })\n                .thenAccept(__ -> {\n                    log.info().attr(\"clientAppId\", clientAppId()).attr(\"segment\", segmentTopic)\n                            .log(\"Terminated segment topic\");\n                    asyncResponse.resume(Response.noContent().build());\n                })\n                .exceptionally(ex -> {\n                    log.error().attr(\"clientAppId\", clientAppId()).attr(\"segment\", segmentTopic)\n                            .exception(ex).log(\"Failed to terminate segment topic\");\n                    resumeAsyncResponseExceptionally(asyncResponse, ex);\n                    return null;","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Segments.java#L145-L181","documentation":"HTTP 404 thrown by the segment terminate endpoint when the segment topic resolves but is not loaded on any broker (getTopicIfExists returns empty). Segment topics are never auto-created, and terminate requires a live in-memory topic instance on the owning broker.","triggerScenarios":"Calling the terminateSegment REST endpoint for a segment whose topic has not been loaded/owned by this broker path — e.g. after broker restart, before first use, after the segment was deleted, or a wrong segment hash-range/segment-id in the composed segment name.","commonSituations":"Terminating a segment of a freshly created scalable topic whose segment topic was unloaded; race between segment deletion and a terminate call; mistyped segment topic name; broker ownership moved and the redirect/ownership check passed but load hadn't completed.","solutions":["Verify the segment topic name (hash range + segment id) against the scalable topic's metadata.","Trigger load of the segment topic (e.g. produce/consume once or use the admin API to look it up) and retry termination.","Confirm the segment still exists in the scalable topic metadata before terminating; if deleted, skip it."],"exampleFix":"// before\nawait admin.scalableTopics().terminateSegment(segmentTopicName); // 404 not loaded\n// after\nconst md = await admin.scalableTopics().getScalableTopicMetadataAsync(tenant, ns, topic);\nif (md.getSegments().containsKey(segmentId)) {\n    await admin.scalableTopics().terminateSegment(segmentTopicName);\n}","handlingStrategy":"retry","validationCode":"const md = await admin.scalableTopics().getScalableTopicMetadataAsync(tenant, ns, parentTopic);\nif (!md.getSegments().containsKey(segmentId)) throw new Error('segment does not exist in metadata');","typeGuard":"const segmentIsKnown = (metadata, segId) => metadata?.getSegments?.().has(segId) ?? false;","tryCatchPattern":"try {\n  await admin.scalableTopics().terminateSegment(segmentTopic);\n} catch (e) {\n  if (e.status === 404) { await sleep(loadRetryMs); return admin.scalableTopics().terminateSegment(segmentTopic); }\n  throw e;\n}","preventionTips":["Build segment names from the scalable topic metadata (hash range + segment id), never by hand.","Touch the segment (lookup/reader) to force load before terminating.","Handle broker restarts/failovers by re-trying ownership-dependent calls."],"tags":["rest","not-found","segment","termination","http-404"],"backgroundTag":"topic-not-loaded","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"}