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
- Upgrade all nodes to 4.0+ before enabling transient replication
- Remove/retire nodes running < 4.0 (decommission) and clean stale gossip entries (nodetool assassinate if safe)
- 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
- Finish cluster upgrades before adopting new replication features
- Clean stale gossip entries for permanently retired nodes
- Verify nodetool version across all members before schema changes
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
- Amount of transient nodes should be strictly positive, but…
- Attempted to serve data request from node in
- Attempting to compact transient sstables with non transient…
- Can only safely increase number of transients one at a time…
- Can't add full replicas if there are any transient…
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)