apache/pulsar · error · RestException
Cannot terminate non-persistent topic: ${segmentTopic}
Error message
Cannot terminate non-persistent topic: ${segmentTopic} What it means
HTTP 400 thrown by terminateSegment when the loaded topic instance is not a PersistentTopic. Termination (sealing the ledger so no more writes are accepted) is only defined for persistent topics, so a non-persistent instance is rejected as a bad request.
Source
Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Segments.java:169
@Parameter(description = "Segment descriptor (e.g. 0000-7fff-1)", required = true)
@PathParam("descriptor") String descriptor,
@Parameter(description = "Whether leader broker redirected this call to this broker.")
@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()))
.thenCompose(optTopic -> {
if (optTopic.isEmpty()) {
throw new RestException(Response.Status.NOT_FOUND,
"Segment topic not found: " + segmentTopic);
}
if (optTopic.get() instanceof PersistentTopic pt) {
return pt.terminate().thenApply(__ -> null);
}
throw new RestException(Response.Status.BAD_REQUEST,
"Cannot terminate non-persistent topic: " + segmentTopic);
})
.thenAccept(__ -> {
log.info().attr("clientAppId", clientAppId()).attr("segment", segmentTopic)
.log("Terminated segment topic");
asyncResponse.resume(Response.noContent().build());
})
.exceptionally(ex -> {
log.error().attr("clientAppId", clientAppId()).attr("segment", segmentTopic)
.exception(ex).log("Failed to terminate segment topic");
resumeAsyncResponseExceptionally(asyncResponse, ex);
return null;
});
}
@PUT
@Path("/{tenant}/{namespace}/{topic}/{descriptor}/subscription/{subscription}")
@Operation(summary = "Create a subscription cursor on the segment topic at the earliest"View on GitHub (pinned to 820761864e)
Solutions
- Ensure the segment topic is addressed with the persistent:// domain — segment topics are always persistent.
- Fix the code building the segment topic name (SegmentTopicName.fromParent) or the URL path so it targets the persistent topic.
- If you need to stop writes on a non-persistent topic, disconnect producers instead — terminate does not apply.
Example fix
// before
TopicName seg = TopicName.get("non-persistent", ns, localName); // 400
// after
TopicName seg = TopicName.get("persistent", ns, localName); Defensive patterns
Strategy: validation
Validate before calling
if (!segmentTopic.toString().startsWith('persistent://')) {
throw new Error('segment topics must use the persistent:// domain');
} Type guard
const isPersistentTopicName = (tn) => typeof tn === 'string' && tn.startsWith('persistent://'); Try / catch
try {
await admin.scalableTopics().terminateSegment(segmentTopic);
} catch (e) {
if (e.status === 400 && /non-persistent/.test(e.message)) { /* fix domain prefix */ }
throw e;
} Prevention
- Always compose segment names via SegmentTopicName.fromParent with the persistent domain.
- Never reuse non-persistent topic handles for segment operations.
- Add a unit check on segment-name construction in your tooling.
When it happens
Trigger: Calling the segment terminate endpoint against a segment topic that materialized as a non-persistent topic — e.g. the segment name maps to a non-persistent domain topic or a misconfigured topic domain, so the broker loaded a NonPersistentTopic instead.
Common situations: Constructing the segment topic name with the wrong domain prefix (non-persistent:// instead of persistent://); namespace-level policies or custom pluggable topic types yielding a different Topic implementation; test setups using non-persistent topics interchangeably.
Related errors
- Segment topic not found: ${segmentTopic}
- Segment topic not loaded: ${segmentTopic}
- Subscription not found on segment: ${subscription}
- Corrupt Function MetaData
- No Function %s found
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/5a40ee91ed9cc5fd.
Report an issue: GitHub.