apache/cassandra · error · Transformation.RejectedTransformationException

There are not enough nodes in

Error message

There are not enough nodes in %s datacenter to satisfy replication factor

What it means

Thrown by CMSPlacementStrategy.reconfigure() as a RejectedTransformationException when a datacenter exists but has fewer registered nodes than the requested replication factor, so the CMS placement cannot satisfy the RF there.

Solutions

  1. Lower the requested RF to at most the node count in each DC
  2. Add more nodes to the datacenter before reconfiguring
  3. Remove the under-provisioned DC from the reconfigure request

Example fix

// before (3 nodes in dc1)
nodetool cms reconfigure --rf 5

// after
nodetool cms reconfigure --rf 3
Defensive patterns

Strategy: validation

Validate before calling

for (Map.Entry<String,Integer> e : requestedRf.entrySet()) {
    int size = metadata.directory.allDatacenterEndpoints().get(e.getKey()).size();
    if (e.getValue() > size) throw new IllegalArgumentException("RF " + e.getValue() + " > " + size + " nodes in " + e.getKey());
}

Try / catch

try { cms.reconfigure(rf); } catch (Transformation.RejectedTransformationException e) { logger.warn("Not enough nodes for RF; reduce RF or add nodes"); }

Prevention

When it happens

Trigger: reconfigure (via prepareNewCMS/newCms) with an RF per DC greater than the number of live endpoints in that DC from metadata.directory.allDatacenterEndpoints().

Common situations: nodetool cms reconfigure 5 in a 3-node DC; requesting RF for a DC still mid-deployment; stale RF map after scaling the cluster down.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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

Appendix: source

Thrown at src/java/org/apache/cassandra/locator/CMSPlacementStrategy.java:75

    @VisibleForTesting
    public CMSPlacementStrategy(Map<String, Integer> rf, BiFunction<ClusterMetadata, NodeId, Boolean> filter)
    {
        // todo: verify only test uses with other filter
        this.rf = rf;
        this.filter = filter;
    }

    public Set<NodeId> reconfigure(ClusterMetadata metadata)
    {
        Map<String, ReplicationFactor> rf = new HashMap<>(this.rf.size());
        for (Map.Entry<String, Integer> e : this.rf.entrySet())
        {
            Collection<InetAddressAndPort> nodesInDc = metadata.directory.allDatacenterEndpoints().get(e.getKey());
            if (nodesInDc.isEmpty())
                throw new IllegalStateException(String.format("There are no nodes in %s datacenter", e.getKey()));
            if (nodesInDc.size() < e.getValue())
                throw new Transformation.RejectedTransformationException(String.format("There are not enough nodes in %s datacenter to satisfy replication factor", e.getKey()));

            rf.put(e.getKey(), ReplicationFactor.fullOnly(e.getValue()));
        }

        Directory tmpDirectory = metadata.directory;
        TokenMap tmpTokenMap = metadata.tokenMap;
        for (NodeId peerId : metadata.directory.peerIds())
        {
            if (!filter.apply(metadata, peerId))
            {
                tmpDirectory = tmpDirectory.without(metadata.nextEpoch(), peerId);
                tmpTokenMap = tmpTokenMap.unassignTokens(peerId);
            }
        }

        // Although MetaStrategy has its own entireRange, it uses a custom partitioner which isn't compatible with
        // regular, non-CMS placements. For that reason, we select replicas here using tokens provided by the
        // globally configured partitioner. This also has the benefit of making concurrent operations, such as

View on GitHub (pinned to 88fd0f6a0e)