apache/pulsar · error · RestException

Topic does not exist: ${persistentBase}

Error message

Topic does not exist: ${persistentBase}

What it means

HTTP 404 raised during migration when the source regular (persistent) topic named by the path does not exist. Migration transforms an existing regular topic into a scalable one, so the source must exist before the prechecks can run.

Source

Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/ScalableTopics.java:331

     *       watch);</li>
     *   <li>terminate the old topics so no further v4 writes can land — they become the
     *       drainable sealed parent segments.</li>
     * </ol>
     */
    private CompletableFuture<Void> doMigrateToScalableAsync(TopicName scalableName,
                                                             TopicName persistentBase, boolean force) {
        return resources().getScalableTopicMetadataAsync(scalableName).thenCompose(existing -> {
            if (existing.isPresent()) {
                throw new RestException(Response.Status.CONFLICT,
                        "Topic is already scalable: " + scalableName);
            }
            return pulsar().getNamespaceService().checkTopicExistsAsync(persistentBase);
        }).thenCompose(existsInfo -> {
            boolean exists = existsInfo.isExists();
            int partitions = existsInfo.getPartitions();
            existsInfo.recycle();
            if (!exists) {
                throw new RestException(Response.Status.NOT_FOUND,
                        "Topic does not exist: " + persistentBase);
            }
            CompletableFuture<Void> precheck = force
                    ? CompletableFuture.completedFuture(null)
                    : checkNoLegacyConnectionsAsync(persistentBase, partitions);
            return precheck.thenApply(__ -> partitions);
        }).thenCompose(partitions -> {
            ScalableTopicMetadata metadata =
                    ScalableTopicController.createMigratedMetadata(persistentBase, partitions,
                            pulsar().getConfiguration().getScalableTopicEntryBucketBudget(),
                            pulsar().getConfiguration().getScalableTopicEntryBucketMaxPerSegment());
            return createMigratedChildTopicsAsync(scalableName, metadata)
                    .thenCompose(__ -> resources().createScalableTopicAsync(scalableName, metadata))
                    .thenCompose(__ -> terminateLegacyTopicsAsync(persistentBase, partitions));
        });
    }

    /**

View on GitHub (pinned to 820761864e)

Solutions

  1. Verify the topic exists with GET /admin/v2/persistent/{tenant}/{namespace}/{topic} (or topics list) before migrating.
  2. Correct the topic name, partition suffix, tenant, or namespace in the migration call.
  3. Create the topic first if it never existed, or skip migration entirely for non-existent sources.

Example fix

// before
await admin.scalableTopics().migrateToScalable(tenant, ns, 'my-topc');
// after
const exists = await admin.topics().getStatsAsync('persistent://tn/ns/my-topic');
await admin.scalableTopics().migrateToScalable(tenant, ns, 'my-topic');
Defensive patterns

Strategy: validation

Validate before calling

try {
  await admin.topics().getStatsAsync(`persistent://${tenant}/${ns}/${topic}`);
} catch (e) {
  if (e.status === 404) throw new Error(`Source topic ${topic} does not exist; fix name or skip`);
  throw e;
}

Type guard

const topicExists = async (tn) => { try { await admin.topics().getStatsAsync(tn); return true; } catch (e) { return e.status !== 404; } };

Try / catch

try {
  await admin.scalableTopics().migrateToScalable(tenant, ns, topic);
} catch (e) {
  if (e.status === 404) { /* verify source topic name/existence before retry */ }
  throw e;
}

Prevention

When it happens

Trigger: Calling POST /admin/v2/scalable-topics/{tenant}/{namespace}/{topic}/migrate when the underlying persistent topic was never created, was deleted, or the name is misspelled/mis-partitioned (e.g. wrong partition index).

Common situations: Typos in topic names in migration scripts; migrating a topic whose producers never connected (auto-creation disabled so nothing materialized); migrating after a cleanup job deleted the topic; wrong namespace/tenant in the URL.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/ed5c68434eb461e9. Report an issue: GitHub.