apache/cassandra · warning
Received base materialized view mutation for key
Error message
Received base materialized view mutation for key {} that does not belong to this node. There is probably a range movement happening (move or decommission),but this node hasn't updated its ring metadata yet. Adding mutation to local batchlog to be replayed later. What it means
When applying a base-table mutation that requires a materialized-view update, the coordinator computes which replica pair (base endpoint + view endpoint) should handle it. If no paired endpoint exists for this node's token, ring metadata is out of sync with an in-progress range movement (move/decommission/bootstrap). The mutation is written to the local batchlog for later replay so the view update is not lost.
Solutions
- Wait for ring convergence (nodetool ring/describering show consistent ownership) — batchlog replay handles the mutation automatically
- Ensure 'nodetool drain/ decommission' completed cleanly and all nodes agree on tokens
- Run repair afterwards to reconcile MV rows; the message itself recommends checking range movement state
- If persistent with no movement ongoing, check for gossip partitioning/firewall issues between nodes
Example fix
// before $ nodetool decommission # writes continuing elsewhere hit this warning // after $ nodetool netstats && nodetool describering -- keyspace # confirm ring settled $ nodetool repair -pr keyspace mv_table
Defensive patterns
Strategy: retry
Validate before calling
// before MV-heavy writes during topology changes, confirm ring stability // (via JMX/metadata): pending ranges empty and all nodes agree on ownership assert pendingRanges().isEmpty() : "range movement in progress; defer writes or expect batchlog replay";
Try / catch
try {
executeMvWrite(mutation);
} catch (WriteFailureException | WriteTimeoutException e) {
// mutation was added to local batchlog; verify replay, then repair
scheduleRepair(keyspace, mvTable);
} Prevention
- Quiesce or throttle writes during move/decommission/bootstrap
- Monitor gossip convergence before declaring topology changes complete
- Run repair after every topology change on MV keyspaces
When it happens
Trigger: applyAggregationOnBase likely called during MV write path when pairedEndpoint is absent and pendingReplicas is empty — i.e., the mutation key's token maps to no current+pending replica pair on this node, typically because token metadata hasn't refreshed after a ring change.
Common situations: Decommission, bootstrap, or range move in progress while writes continue; gossip/ring metadata propagation lag between nodes; client hitting a node that hasn't yet learned about new token ownership.
Related errors
- Nothing to repair for
- Allocated token already assigned to node . Is another node…
- Can not commit transformation
- Can not remove a node that has an in-progress sequence
- Can't move to the undefined (null) token.
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/37199b2f1533b3d3.
Report an issue: GitHub.
Appendix: source
Thrown at src/java/org/apache/cassandra/service/StorageProxy.java:1160
BatchlogCleanup cleanup = new BatchlogCleanup(mutations.size(),
() -> asyncRemoveFromBatchlog(localReplicaPlan, batchUUID, requestTime));
// add a handler for each mutation - includes checking availability, but doesn't initiate any writes, yet
for (Mutation mutation : mutations)
{
String keyspaceName = mutation.getKeyspaceName();
Token tk = mutation.key().getToken();
Function<ClusterMetadata, Optional<Replica>> pairedEndpointSupplier = (cm) -> ViewUtils.getViewNaturalEndpoint(cm, keyspaceName, baseToken, tk);
Function<ClusterMetadata, VersionedEndpoints.ForToken>pendingReplicasSupplier = (cm) -> cm.pendingEndpointsFor(Keyspace.open(keyspaceName).getMetadata(), tk);
Optional<Replica> pairedEndpoint = pairedEndpointSupplier.apply(metadata);
VersionedEndpoints.ForToken pendingReplicas = pendingReplicasSupplier.apply(metadata);
// if there are no paired endpoints there are probably range movements going on, so we write to the local batchlog to replay later
if (!pairedEndpoint.isPresent())
{
if (pendingReplicas.isEmpty())
logger.warn("Received base materialized view mutation for key {} that does not belong " +
"to this node. There is probably a range movement happening (move or decommission)," +
"but this node hasn't updated its ring metadata yet. Adding mutation to " +
"local batchlog to be replayed later.",
mutation.key());
continue;
}
// When local node is the endpoint we can just apply the mutation locally,
// unless there are pending endpoints, in which case we want to do an ordinary
// write so the view mutation is sent to the pending endpoint
if (pairedEndpoint.get().isSelf() && StorageService.instance.isJoined()
&& pendingReplicas.isEmpty())
{
try
{
mutation.apply(writeCommitLog);
nonLocalMutations.remove(mutation);
// won't trigger cleanupView on GitHub (pinned to 88fd0f6a0e)