apache/cassandra · error · IllegalArgumentException

Transient replication is not supported in mixed version…

Error message

Transient replication is not supported in mixed version clusters with nodes < 4.0. Bad nodes: 

What it means

Transient replication requires all cluster nodes to understand transient replicas, which only nodes at major version 4.0+ support. ReplicationFactor.validate checks all live and unreachable gossip members (excluding the local node) and throws IllegalArgumentException if any member reports a release version older than 4.0.

Solutions

  1. Upgrade all nodes to 4.0+ before enabling transient replication
  2. Remove/retire nodes running < 4.0 (decommission) and clean stale gossip entries (nodetool assassinate if safe)
  3. Verify versions with `nodetool version` / `nodetool gossipinfo` on every member

Example fix

// before
CREATE KEYSPACE ks WITH replication = {'class':'NetworkTopologyStrategy','dc1':'3/1'}; // cluster has 3.11 nodes
// after
-- upgrade all nodes to 4.0+ first, then:
CREATE KEYSPACE ks WITH replication = {'class':'NetworkTopologyStrategy','dc1':'3/1'};
Defensive patterns

Strategy: validation

Validate before calling

boolean allAtLeast4 = Stream.concat(Gossiper.instance.getLiveMembers().stream(), Gossiper.instance.getUnreachableMembers().stream())
    .filter(ep -> !ep.equals(FBUtilities.getBroadcastAddressAndPort()))
    .map(ep -> Gossiper.instance.getReleaseVersion(ep))
    .allMatch(v -> v == null || v.major >= 4);
if (!allAtLeast4) throw new IllegalStateException("Upgrade all nodes to 4.0+ before transient replication");

Try / catch

try { session.execute(createKeyspaceCql); }
catch (InvalidQueryException e) { if (e.getMessage().contains("mixed version")) { /* complete upgrades first */ } }

Prevention

When it happens

Trigger: Enabling transient replication (RF with '/n' transient part, e.g. 5/2) while the cluster still contains nodes running a < 4.0 release version, including nodes that are down but still tracked in gossip as unreachable members.

Common situations: Upgrading a pre-4.0 cluster and attempting to use transient replication before all nodes are upgraded; a dead node's stale gossip entry pinning an old release version.

Related errors


AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10). Data as JSON: /api/errors/97694d331055a018. Report an issue: GitHub.

Appendix: source

Thrown at src/java/org/apache/cassandra/locator/ReplicationFactor.java:79

    static void validate(int totalRF, int transientRF)
    {
        Preconditions.checkArgument(transientRF == 0 || DatabaseDescriptor.isTransientReplicationEnabled(),
                                    "Transient replication is not enabled on this node");
        Preconditions.checkArgument(totalRF >= 0,
                                    "Replication factor must be non-negative, found %s", totalRF);
        Preconditions.checkArgument(transientRF == 0 || transientRF < totalRF,
                                    "Transient replicas must be zero, or less than total replication factor. For %s/%s", totalRF, transientRF);
        if (transientRF > 0)
        {
            Preconditions.checkArgument(DatabaseDescriptor.getNumTokens() == 1,
                                        "Transient nodes are not allowed with multiple tokens");
            Stream<InetAddressAndPort> endpoints = Stream.concat(Gossiper.instance.getLiveMembers().stream(), Gossiper.instance.getUnreachableMembers().stream());
            List<InetAddressAndPort> badVersionEndpoints = endpoints.filter(Predicates.not(FBUtilities.getBroadcastAddressAndPort()::equals))
                                                                    .filter(endpoint -> Gossiper.instance.getReleaseVersion(endpoint) != null && Gossiper.instance.getReleaseVersion(endpoint).major < 4)
                                                                    .collect(Collectors.toList());
            if (!badVersionEndpoints.isEmpty())
                throw new IllegalArgumentException("Transient replication is not supported in mixed version clusters with nodes < 4.0. Bad nodes: " + badVersionEndpoints);
        }
        else if (transientRF < 0)
        {
            throw new IllegalArgumentException(String.format("Amount of transient nodes should be strictly positive, but was: '%d'", transientRF));
        }
    }

    public boolean equals(Object o)
    {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        ReplicationFactor that = (ReplicationFactor) o;
        return allReplicas == that.allReplicas && fullReplicas == that.fullReplicas;
    }

    public int hashCode()
    {
        return Objects.hash(allReplicas, fullReplicas);

View on GitHub (pinned to 88fd0f6a0e)