apache/cassandra · error · IllegalStateException
Can't do any consensus migrations to/from PaxosV1, switch…
Error message
Can't do any consensus migrations to/from PaxosV1, switch to V2 first
What it means
Consensus (Paxos) migration can only be performed when Paxos V2 is in use. If a node has paxos_variant set to v1 (or the legacy default), starting a migration to any consensus protocol throws this IllegalStateException, because V1 semantics are incompatible with migration state transitions.
Solutions
- Set paxos_variant: v2 in cassandra.yaml on all nodes and restart them (rolling restart is fine)
- Verify with a node check that Paxos V2 is active, then retry the migration command
- Complete any pending Paxos V1 state (all in-flight LWTs drained) before switching
- On mixed-version clusters, finish the upgrade so every node supports V2 before migrating
Example fix
// cassandra.yaml // before paxos_variant: v1 // after paxos_variant: v2
Defensive patterns
Strategy: validation
Validate before calling
// before migrating, on each node verify:
String variant = System.getProperty("cassandra.paxos_variant") /* or check cassandra.yaml */;
if (!"v2".equals(variant)) throw new IllegalStateException("Set paxos_variant=v2 before consensus migration"); Try / catch
try { migration.start(); } catch (IllegalStateException e) { /* reconfigure paxos_variant=v2, rolling restart, retry */ } Prevention
- Set paxos_variant: v2 in cassandra.yaml on all nodes before planning migrations
- Complete version upgrades before enabling migration features
- Document config prerequisites for migration runbooks
When it happens
Trigger: Calling startMigrationToConsensusProtocol (e.g. via ALTER TABLE ... WITH transactional_migration or the migration tooling) while Paxos.useV2() is false — i.e. the cluster/node is configured with paxos_variant=v1 or unset on a version where V2 is not the default.
Common situations: Upgrading an old cluster where paxos_variant was explicitly pinned to v1 in cassandra.yaml; mixed-version cluster during rolling upgrade before all nodes support V2; operator forgetting to switch to V2 before initiating Accord/migration.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- paxos_cache_size option was set incorrectly to
- This node was started with paxos variant
- a hints file cannot be configured for both compression and…
- A maximum number of tokens per node is supported
- accord.cache_size option was set incorrectly to
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/355b511eca33be69.
Report an issue: GitHub.
Appendix: source
Thrown at src/java/org/apache/cassandra/service/consensus/migration/ConsensusTableMigration.java:200
if (keyspaceNames == null || keyspaceNames.isEmpty())
{
keyspaceNames = ImmutableList.copyOf(StorageService.instance.getNonLocalStrategyKeyspaces());
}
checkState(keyspaceNames.size() == 1 || !maybeTables.isPresent(), "Can't specify tables with multiple keyspaces");
List<TableId> ids = keyspacesAndTablesToTableIds(cm, keyspaceNames, maybeTables);
List<TableId> tableIds = new ArrayList<>();
for (TableId tableId : ids)
{
TableMetadata metadata = cm.schema.getTableMetadata(tableId);
if (metadata == null || !metadata.params.transactionalMigrationFrom.isMigrating())
continue;
tableIds.add(tableId);
}
if (!Paxos.useV2())
throw new IllegalStateException("Can't do any consensus migrations to/from PaxosV1, switch to V2 first");
IPartitioner partitioner = DatabaseDescriptor.getPartitioner();
Optional<List<Range<Token>>> maybeParsedRanges = maybeRangesStr.map(rangesStr -> ImmutableList.copyOf(RepairOption.parseRanges(rangesStr, partitioner)));
Token minToken = partitioner.getMinimumToken();
NormalizedRanges<Token> ranges = normalizedRanges(maybeParsedRanges.orElse(ImmutableList.of(new Range(minToken, minToken))));
ClusterMetadataService.instance().commit(new BeginConsensusMigrationForTableAndRange(ranges, tableIds));
}
public static Integer finishMigrationToConsensusProtocol(@Nonnull String keyspace,
@Nonnull Optional<List<String>> maybeTables,
@Nonnull Optional<String> maybeRangesStr,
@Nonnull ConsensusMigrationTarget target)
{
checkArgument(!maybeTables.isPresent() || !maybeTables.get().isEmpty(), "Must provide at least 1 table if Optional is not empty");
checkNotNull(target);
ClusterMetadata cm = ClusterMetadata.current();
View on GitHub (pinned to 88fd0f6a0e)