apache/seatunnel · error · PulsarConnectorException

PulsarConnectorErrorCode.GET_LAST_CURSOR_FAILED

PulsarConnectorErrorCode.GET_LAST_CURSOR_FAILED

Error message

Failed to get the last cursor

What it means

LatestMessageStopCursor.prepare fetches the topic's last message ID via the PulsarAdmin client so the split knows where to stop. If admin.topics().getLastMessageId(topic) throws a PulsarAdminException, it is wrapped as GET_LAST_CURSOR_FAILED with this message. Reading fails at split-prepare time because the stop position cannot be determined.

Source

Thrown at seatunnel-connectors-v2/connector-pulsar/src/main/java/org/apache/seatunnel/connectors/seatunnel/pulsar/source/enumerator/cursor/stop/LatestMessageStopCursor.java:46

import org.apache.pulsar.client.api.MessageId;

/**
 * A stop cursor that initialize the position to the latest message id. The offsets initialization
 * are taken care of by the {@code PulsarPartitionSplitReaderBase} instead of by the {@code
 * PulsarSourceEnumerator}.
 */
public class LatestMessageStopCursor implements StopCursor {
    private static final long serialVersionUID = 1L;

    private MessageId messageId;

    public void prepare(PulsarAdmin admin, TopicPartition partition) {
        if (messageId == null) {
            String topic = partition.getFullTopicName();
            try {
                messageId = admin.topics().getLastMessageId(topic);
            } catch (PulsarAdminException e) {
                throw new PulsarConnectorException(
                        PulsarConnectorErrorCode.GET_LAST_CURSOR_FAILED,
                        "Failed to get the last cursor",
                        e);
            }
        }
    }

    @Override
    public boolean shouldStop(Message<?> message) {
        MessageId id = message.getMessageId();
        return id.compareTo(messageId) >= 0;
    }

    @Override
    public StopCursor copy() {
        return new LatestMessageStopCursor();
    }
}

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Verify admin.service-url and Pulsar admin credentials are correct and reachable from the worker.
  2. Confirm the topic exists and the configured topic name matches exactly (including persistent:// domain/tenant/namespace/topic).
  3. Check broker logs for the underlying PulsarAdminException cause (TopicNotFound, NotAuthorized, etc.).
  4. For partitioned topics, ensure the partition reference used matches an existing partition.

Example fix

// before
admin.service-url = "http://broker:8081"
// after (correct admin port/service URL)
admin.service-url = "http://broker:8080"
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-flight: topic must exist and admin reachable
admin.namespaces().getTopics(namespace).contains(fullTopicName);

Try / catch

try {
    prepareStopCursor(admin, partition);
} catch (PulsarConnectorException e) {
    if (e.getErrorCode() == PulsarConnectorErrorCode.GET_LAST_CURSOR_FAILED) {
        // check cause: TopicNotFound -> fix topic name; auth -> fix credentials
    }
    throw e;
}

Prevention

When it happens

Trigger: Using stop.mode = LATEST when PulsarAdmin.topics().getLastMessageId fails for the full topic name — broker unreachable, topic does not exist, permission denied, or topic name (partitioned topic suffix) is malformed.

Common situations: Misconfigured admin.service-url or authentication; typo in topic name; reading a partitioned topic where the partition suffix handling mismatches; topic deleted between discovery and reader start.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/374843510a7c4622. Report an issue: GitHub.