apache/pulsar · info · RestException
Migration has already been completed
Error message
Migration has already been completed
What it means
A 409 CONFLICT thrown when starting a metadata migration whose stored state phase is already COMPLETED. The migration is idempotent-by-guard: once finished, the flag in the source store permanently records COMPLETED and further starts are rejected instead of re-running.
Source
Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/MetadataMigrationBase.java:112
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
}
}
}
// Create coordinator
MigrationCoordinator coordinator = new MigrationCoordinator(pulsar().getLocalMetadataStore(), targetUrl);
// Start migration in background thread
pulsar().getExecutor().submit(() -> {
try {
log.info().attr("targetUrl", targetUrl).log("Starting metadata migration");
coordinator.startMigration();
log.info("Metadata migration completed successfully");
} catch (Exception e) {
log.error().exception(e).log("Metadata migration failed");View on GitHub (pinned to 820761864e)
Solutions
- No action needed — the migration is done; verify the broker is reading from the target store.
- Use the migration status endpoint to confirm phase COMPLETED instead of starting again.
- If a re-migration is truly required, follow the documented reset procedure (which implies reconfiguring stores/flag), not the start endpoint.
Example fix
// before: unconditional start after DR runbook step
startMigration(target); // 409
// after: skip when already completed
if (getMigrationStatus().getPhase() != Phase.COMPLETED) {
startMigration(target);
} Defensive patterns
Strategy: validation
Validate before calling
if (getMigrationStatus().getPhase() == Phase.COMPLETED) {
return; // migration already finished, skip start
} Try / catch
try { startMigration(target); }
catch (PulsarAdminException e) {
if (e.getStatusCode() == 409 && e.getMessage().contains("completed")) {
// treat as success / no-op
} else throw e;
} Prevention
- Treat COMPLETED as success in runbooks and scripts
- Check status before start in idempotent automation
- Never re-run startMigration after a successful migration; use documented reset procedures for re-migration
When it happens
Trigger: Re-invoking the migration start endpoint after a prior migration finished successfully; replaying an idempotent automation script; stale client state unaware the migration already ran.
Common situations: Post-migration verification scripts accidentally calling start again; disaster-recovery runbooks that include the start step without checking state; expected behavior after a successful migration.
Related errors
- Migration is already in progress (phase: ${phase})
- Cluster already exists
- ${duplicateBrokers} already exists in ${domainName}
- Target URL is required
- Metadata store is not configured for migration. Please ensur
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/d2c26dea3f2b1399.
Report an issue: GitHub.