apache/cassandra · error · TruncateException

Truncate failed on replica

Error message

Truncate failed on replica {endpoint} -> {failureReason}

What it means

Thrown by TruncateResponseHandler.get() as TruncateException when at least one replica explicitly reported a non-timeout failure reason for the truncation. The message lists each failing replica and its RequestFailureReason so the operator knows which nodes failed and why.

Solutions

  1. Read the replica endpoints in the message and check those nodes' logs for the underlying truncation error.
  2. Fix the replica-side issue (disk space, restart, repair) and retry the TRUNCATE.
  3. If a replica is persistently unhealthy, replace it (nodetool decommission/rebuild) and re-run truncate.
  4. Check for version incompatibilities if failure reasons look protocol-related after an upgrade.
Defensive patterns

Strategy: try-catch

Validate before calling

// check disk space and liveness of each replica before truncating
replicas.forEach(r -> assertReplicaHealthy(r, minFreeDisk, truncateRequestTimeoutMs));

Try / catch

catch (TruncateException e) {
    Set<InetAddressAndPort> failed = parseFailedReplicas(e.getMessage());
    failed.forEach(r -> alertOps("Replica failed truncate", r));
    throw e; // do not assume data was removed
}

Prevention

When it happens

Trigger: TRUNCATE where a replica sends a failure response with a concrete RequestFailureReason (e.g. UNKNOWN, node crashing during truncation, replica-local TruncateException) back to the coordinator.

Common situations: A replica crashed or restarted mid-truncation, disk full on a replica, replica threw while dropping SSTables/memtables, or mixed-version cluster where a replica cannot process the truncate verb.

Related errors


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

Appendix: source

Thrown at src/java/org/apache/cassandra/service/TruncateResponseHandler.java:90

        {
            throw new UncheckedInterruptedException(e);
        }

        if (!signaled)
            throw new TimeoutException("Truncate timed out - received only " + responses.get() + " responses");

        if (!failureReasonByEndpoint.isEmpty())
        {
            // clone to make sure no race condition happens
            Map<InetAddressAndPort, RequestFailureReason> failureReasonByEndpoint = new HashMap<>(this.failureReasonByEndpoint);
            if (RequestCallback.isTimeout(failureReasonByEndpoint))
                throw new TimeoutException("Truncate timed out - received only " + responses.get() + " responses");

            StringBuilder sb = new StringBuilder("Truncate failed on ");
            for (Map.Entry<InetAddressAndPort, RequestFailureReason> e : failureReasonByEndpoint.entrySet())
                sb.append("replica ").append(e.getKey()).append(" -> ").append(e.getValue()).append(", ");
            sb.setLength(sb.length() - 2);
            throw new TruncateException(sb.toString());
        }
    }

    @Override
    public void onResponse(Message<TruncateResponse> message)
    {
        responses.incrementAndGet();
        if (responses.get() >= responseCount)
            condition.signalAll();
    }

    @Override
    public void onFailure(InetAddressAndPort from, RequestFailure failure)
    {
        // If the truncation hasn't succeeded on some replica, abort and indicate this back to the client.
        failureReasonByEndpoint.put(from, failure.reason);
        condition.signalAll();
    }

View on GitHub (pinned to 88fd0f6a0e)