apache/pulsar · error · RestException
Metadata store is not configured for migration. Please ensur
Error message
Metadata store is not configured for migration. Please ensure you're using a supported source metadata store (e.g., ZooKeeper).
What it means
A 400 BAD_REQUEST thrown when the broker's local metadata store is not a DualMetadataStore, i.e. the broker was not started with a migration-capable (dual/source-wrapped) metadata store configuration. Migration can only be orchestrated through a DualMetadataStore that fronts both source and target stores.
Source
Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/MetadataMigrationBase.java:98
@ApiResponse(responseCode = "204", description = "Migration started successfully"),
@ApiResponse(responseCode = "400", description = "Invalid target URL"),
@ApiResponse(responseCode = "409", description = "Migration already in progress"),
@ApiResponse(responseCode = "500", description = "Internal server error")
})
public void startMigration(
@Parameter(description = "Target metadata store URL", required = true)
@QueryParam("target")
String targetUrl) {
validateSuperUserAccess();
if (targetUrl == null || targetUrl.trim().isEmpty()) {
throw new RestException(Response.Status.BAD_REQUEST, "Target URL is required");
}
try {
// Check if metadata store is wrapped with DualMetadataStore
if (!(pulsar().getLocalMetadataStore() instanceof DualMetadataStore dualStore)) {
throw new RestException(Response.Status.BAD_REQUEST, "Metadata store is not configured for migration. "
+ "Please ensure you're using a supported source metadata store (e.g., ZooKeeper).");
}
// Reject the request if a migration is already in progress or was completed. The migration
// flag is always kept in the source store, so read it from there: after a completed
// migration the dual store would route the read to the target store.
var existingFlag = dualStore.getSourceStore().get(MigrationState.MIGRATION_FLAG_PATH).get();
if (existingFlag.isPresent()) {
MigrationState currentState = ObjectMapperFactory.getMapper().reader()
.readValue(existingFlag.get().getValue(), MigrationState.class);
switch (currentState.getPhase()) {
case PREPARATION, COPYING -> throw new RestException(Response.Status.CONFLICT,
"Migration is already in progress (phase: " + currentState.getPhase() + ")");
case COMPLETED -> throw new RestException(Response.Status.CONFLICT,
"Migration has already been completed");
default -> {
// NOT_STARTED or FAILED: ok to start (or retry) the migration
}View on GitHub (pinned to 820761864e)
Solutions
- Reconfigure the broker to use the dual metadata store wrapper so pulsar().getLocalMetadataStore() instanceof DualMetadataStore, then restart.
- Target the request at a broker that is actually configured for migration.
- Verify with broker logs/config which metadata store implementation is active at startup.
Example fix
# before: default store only metadataStoreUrl=zookeeper:zk1:2181 # after: configure migration/dual store mode per migration docs so the broker # instantiates DualMetadataStore wrapping ZooKeeper (source) and target store # then restart brokers before calling the migration endpoint
Defensive patterns
Strategy: validation
Validate before calling
// ensure brokers run the dual metadata store before calling migration APIs // (no public API check; verify config at deploy time) assert brokerConf.metadataStoreConfiguredForMigration() : "configure DualMetadataStore first";
Try / catch
try { startMigration(target); }
catch (PulsarAdminException e) {
if (e.getStatusCode() == 400 && e.getMessage().contains("not configured for migration")) {
/* reconfigure broker with DualMetadataStore and restart */
} else throw e;
} Prevention
- Deploy migration-mode brokers with DualMetadataStore wrapping the source (e.g. ZooKeeper) store
- Document which broker clusters are migration-capable
- Point migration tooling at migration-capable brokers only
When it happens
Trigger: Calling the migration start endpoint on a broker whose configurationMetadataStore/localMetadataStore is a plain store (e.g. default ZooKeeper store, not wrapped in DualMetadataStore via the migration configuration).
Common situations: Running migration admin commands against a normal (non-migration) broker deployment; forgetting to enable the dual metadata store wrapper in broker.conf (metadataStoreUrl / metadataStore config for migration mode); hitting the wrong broker cluster.
Related errors
- Target URL is required
- Timeout during mark-delete operation
- Timeout during clear backlog operation
- Timeout during skip messages operation
- Timeout during delete operation
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/55ad008dec059f79.
Report an issue: GitHub.