apache/cassandra · warning
Executing a LOGGED BATCH on table
Error message
Executing a LOGGED BATCH on table{} {}, configured with a gc_grace_seconds of 0. The gc_grace_seconds is used to TTL batchlog entries, so setting gc_grace_seconds too low on tables involved in an atomic batch might cause batchlog entries to expire before being replayed. What it means
BatchStatement warns (NoSpamLogger at most once per minute) when a LOGGED batch writes to tables configured with gc_grace_seconds=0. In a logged batch, gc_grace_seconds governs batchlog entry TTL, so 0 lets batchlog entries expire before replay can fix partial batch application, weakening atomicity.
Solutions
- Raise gc_grace_seconds on tables used in LOGGED batches (e.g. 864000 for 1 day)
- Switch to UNLOGGED batches if atomicity across tables is not required
- Restructure the data model to avoid multi-partition atomic batches entirely
Example fix
// before CREATE TABLE t (k int PRIMARY KEY, v text) WITH gc_grace_seconds = 0; // after CREATE TABLE t (k int PRIMARY KEY, v text) WITH gc_grace_seconds = 86400;
Defensive patterns
Strategy: validation
Validate before calling
// Schema review check before using LOGGED batches on a table
function assertSafeForLoggedBatch(tableSchema) {
const ggs = tableSchema.options['gc_grace_seconds'];
if (ggs === 0) throw new Error('gc_grace_seconds=0 on table used in LOGGED batch: ' + tableSchema.name);
} Prevention
- Default gc_grace_seconds to 864000 unless you have a documented reason for 0
- Reserve LOGGED batches for small, few-partition atomic writes
- Audit schema for gc_grace_seconds=0 tables used in batches
When it happens
Trigger: Executing BEGIN ... APPLY BATCH (logged) via getMutations() (mutations/executeInternalWithoutConditions paths) where any mutated table has gc_grace_seconds set to 0.
Common situations: Using LOGGED batches on TTL/counter/leveled tables tuned with gc_grace_seconds: 0 for read-repair reasons; copy-pasted schema defaults from non-batch workloads; batch misuse for large multi-partition writes.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- Batch for is of size , exceeding specified threshold of by .
- Batch too large
- Batch with conditions cannot span multiple partitions
- Batch with conditions cannot span multiple tables
- Cannot alter gc_grace_seconds of a materialized view to 0…
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/c0f2976af5bf7862.
Report an issue: GitHub.
Appendix: source
Thrown at src/java/org/apache/cassandra/cql3/statements/BatchStatement.java:412
{
ModificationStatement statement = statements.get(i);
if (isLogged() && statement.metadata().params.gcGraceSeconds == 0)
{
if (tablesWithZeroGcGs == null)
tablesWithZeroGcGs = new HashSet<>();
tablesWithZeroGcGs.add(statement.metadata.toString());
}
QueryOptions statementOptions = options.forStatement(i);
long timestamp = attrs.getTimestamp(batchTimestamp, statementOptions);
statement.addUpdates(collector, partitionKeys.get(i), state, statementOptions, local, timestamp, nowInSeconds, requestTime);
}
if (tablesWithZeroGcGs != null)
{
String suffix = tablesWithZeroGcGs.size() == 1 ? "" : "s";
NoSpamLogger.log(logger, NoSpamLogger.Level.WARN, 1, TimeUnit.MINUTES, LOGGED_BATCH_LOW_GCGS_WARNING,
suffix, tablesWithZeroGcGs);
ClientWarn.instance.warn(MessageFormatter.arrayFormat(LOGGED_BATCH_LOW_GCGS_WARNING, new Object[] { suffix, tablesWithZeroGcGs })
.getMessage());
}
// local is either executeWithoutConditions modifying a virtual table (doesn't support txns) or executeLocal
// which is called by test or internal things that are bypassing distributed system modification/checks
return collector.toMutations(state, local ? PotentialTxnConflicts.ALLOW : PotentialTxnConflicts.DISALLOW);
}
/**
* Checks batch size to ensure threshold is met. If not, a warning is logged.
*
* @param mutations - the batch mutations.
*/
private static void verifyBatchSize(Collection<? extends IMutation> mutations) throws InvalidRequestException
{
// We only warn for batch spanning multiple mutations (#10876)
if (mutations.size() <= 1)
return;
View on GitHub (pinned to 88fd0f6a0e)