apache/pulsar · info · RestException
Message not found
Error message
Message not found
What it means
When generating the HTTP response for a peeked entry, the broker catches a NullPointerException thrown while constructing the response (e.g. the entry payload/metadata was null or already released) and converts it into a 404 NOT_FOUND RestException with message 'Message not found'. It signals that although a peek was attempted, no retrievable message exists at the requested position.
Source
Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/PersistentTopicsBase.java:3110
"Peek messages on a non-persistent topic is not allowed");
} else {
if (subName.startsWith(((PersistentTopic) topic).getReplicatorPrefix())) {
PersistentReplicator repl = getReplicatorReference(subName, (PersistentTopic) topic);
entry = repl.peekNthMessage(messagePosition);
} else {
entry = findOrCreateSubscriptionAsync(subName, (PersistentTopic) topic)
.thenCompose(sub -> sub.peekNthMessage(messagePosition));
}
}
return entry.thenApply(e -> Pair.of(e, (PersistentTopic) topic));
}).thenCompose(entryTopicPair -> {
Entry entry = entryTopicPair.getLeft();
PersistentTopic persistentTopic = entryTopicPair.getRight();
try {
Response response = generateResponseWithEntry(entry, persistentTopic);
return CompletableFuture.completedFuture(response);
} catch (NullPointerException npe) {
throw new RestException(Status.NOT_FOUND, "Message not found");
} catch (Exception exception) {
log.error()
.attr("position", messagePosition)
.attr("topic", topicName)
.attr("subscription", subName)
.exception(exception)
.log("Failed to peek message");
throw new RestException(exception);
} finally {
if (entry != null) {
entry.release();
}
}
});
}
protected CompletableFuture<Response> internalExamineMessageAsync(String initialPosition, long messagePosition,
boolean authoritative) {View on GitHub (pinned to 820761864e)
Solutions
- Retry the peek; transient races often clear on the next attempt
- Validate the position is within the topic's retained entry range (getNumberOfEntries / stats) before peeking
- Check message TTL and retention policies if entries disappear quickly
Example fix
// before
Response r = admin.topics().peekNthMessage(topic, "sub", 1_000_000);
// after
long total = admin.topics().getStats(topic).getNumberOfEntries();
if (position < total) { Response r = admin.topics().peekNthMessage(topic, "sub", (int) position); } Defensive patterns
Strategy: retry
Validate before calling
// check position is within retained range
TopicStats stats = admin.topics().getStats(topic);
if (position >= stats.getNumberOfEntries()) throw new IllegalArgumentException("position out of range"); Try / catch
try { admin.topics().peekNthMessage(topic, sub, position); }
catch (PulsarAdminException e) {
if (e.getStatusCode() == 404) { /* message gone: rescan or skip */ }
} Prevention
- Validate position against current entry count
- Watch TTL/retention settings that delete entries concurrently
When it happens
Trigger: generateResponseWithEntry(null/invalid entry) inside the peek path; concurrent cursor advancement or retention deleting the entry between lookup and response generation; requesting a position beyond the retained range.
Common situations: Peeking with a large position on a topic with fewer entries; message expiry/compaction removing the entry mid-request; race between a consumer ack and an admin peek.
Related errors
- Tenant not found
- Namespace does not exist
- NamespaceIsolationPolicies for cluster ${cluster} does not e
- Cannot find NamespaceIsolationPolicy ${policyName} for clust
- Unauthorized to validateClusterOperation for originalPrincip
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/4a2675bf01b3f19c.
Report an issue: GitHub.