{"record":{"id":"97694d331055a018","repo":"apache/cassandra","slug":"transient-replication-is-not-supported-in-mixed-ve","errorCode":null,"errorMessage":"Transient replication is not supported in mixed version clusters with nodes < 4.0. Bad nodes: ","messagePattern":"Transient replication is not supported in mixed version clusters with nodes < 4\\.0\\. Bad nodes: ","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/locator/ReplicationFactor.java","lineNumber":79,"sourceCode":"\n    static void validate(int totalRF, int transientRF)\n    {\n        Preconditions.checkArgument(transientRF == 0 || DatabaseDescriptor.isTransientReplicationEnabled(),\n                                    \"Transient replication is not enabled on this node\");\n        Preconditions.checkArgument(totalRF >= 0,\n                                    \"Replication factor must be non-negative, found %s\", totalRF);\n        Preconditions.checkArgument(transientRF == 0 || transientRF < totalRF,\n                                    \"Transient replicas must be zero, or less than total replication factor. For %s/%s\", totalRF, transientRF);\n        if (transientRF > 0)\n        {\n            Preconditions.checkArgument(DatabaseDescriptor.getNumTokens() == 1,\n                                        \"Transient nodes are not allowed with multiple tokens\");\n            Stream<InetAddressAndPort> endpoints = Stream.concat(Gossiper.instance.getLiveMembers().stream(), Gossiper.instance.getUnreachableMembers().stream());\n            List<InetAddressAndPort> badVersionEndpoints = endpoints.filter(Predicates.not(FBUtilities.getBroadcastAddressAndPort()::equals))\n                                                                    .filter(endpoint -> Gossiper.instance.getReleaseVersion(endpoint) != null && Gossiper.instance.getReleaseVersion(endpoint).major < 4)\n                                                                    .collect(Collectors.toList());\n            if (!badVersionEndpoints.isEmpty())\n                throw new IllegalArgumentException(\"Transient replication is not supported in mixed version clusters with nodes < 4.0. Bad nodes: \" + badVersionEndpoints);\n        }\n        else if (transientRF < 0)\n        {\n            throw new IllegalArgumentException(String.format(\"Amount of transient nodes should be strictly positive, but was: '%d'\", transientRF));\n        }\n    }\n\n    public boolean equals(Object o)\n    {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        ReplicationFactor that = (ReplicationFactor) o;\n        return allReplicas == that.allReplicas && fullReplicas == that.fullReplicas;\n    }\n\n    public int hashCode()\n    {\n        return Objects.hash(allReplicas, fullReplicas);","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/locator/ReplicationFactor.java#L61-L97","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nCREATE KEYSPACE ks WITH replication = {'class':'NetworkTopologyStrategy','dc1':'3/1'}; // cluster has 3.11 nodes\n// after\n-- upgrade all nodes to 4.0+ first, then:\nCREATE KEYSPACE ks WITH replication = {'class':'NetworkTopologyStrategy','dc1':'3/1'};","handlingStrategy":"validation","validationCode":"boolean allAtLeast4 = Stream.concat(Gossiper.instance.getLiveMembers().stream(), Gossiper.instance.getUnreachableMembers().stream())\n    .filter(ep -> !ep.equals(FBUtilities.getBroadcastAddressAndPort()))\n    .map(ep -> Gossiper.instance.getReleaseVersion(ep))\n    .allMatch(v -> v == null || v.major >= 4);\nif (!allAtLeast4) throw new IllegalStateException(\"Upgrade all nodes to 4.0+ before transient replication\");","typeGuard":null,"tryCatchPattern":"try { session.execute(createKeyspaceCql); }\ncatch (InvalidQueryException e) { if (e.getMessage().contains(\"mixed version\")) { /* complete upgrades first */ } }","preventionTips":["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"],"tags":["transient-replication","version-compatibility","cluster-upgrade"],"backgroundTag":"version-incompatible","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}