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

  1. Set paxos_variant: v2 in cassandra.yaml on all nodes and restart them (rolling restart is fine)
  2. Verify with a node check that Paxos V2 is active, then retry the migration command
  3. Complete any pending Paxos V1 state (all in-flight LWTs drained) before switching
  4. 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

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


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)