apache/cassandra · error · IOException
Term - ' ' belongs to more than keys in mode, which is not…
Error message
Term - '%s' belongs to more than %d keys in %s mode, which is not allowed.
What it means
SASI's OnDiskIndexBuilder refuses to build an index segment in SPARSE mode when a single term maps to more than MAX_KEYS_SPARSE keys. SPARSE mode uses a compact token-tree encoding that assumes low cardinality per term, so a term exceeding the key cap would produce an oversized, inefficient structure; the builder throws IOException instead of silently degrading the index format.
Solutions
- Remove the low-cardinality column from SASI indexing or index it with a different kind/mode so it is not built in SPARSE mode
- Reduce the number of rows sharing that term per index segment (smaller memtable/sstable, more frequent flushes)
- Check MAX_KEYS_SPARSE in OnDiskIndexBuilder and confirm no local modification lowered it below your data's cardinality
- Rebuild the affected SASI index after fixing the data or configuration
Example fix
// before CREATE CUSTOM INDEX ON ks.tbl (status) USING 'org.apache.cassandra.index.sasi.SASIIndex'; // data has 10M rows with status='ACTIVE' -> SPARSE cap exceeded // after // drop and re-create without indexing the low-cardinality column, or use a regular (non-SASI) index: CREATE INDEX ON ks.tbl (status);
Defensive patterns
Strategy: validation
Validate before calling
// Before creating a SASI index on a column, estimate per-term cardinality cqlsh> SELECT COUNT(*) FROM ks.tbl WHERE status = 'ACTIVE'; // If counts per value approach MAX_KEYS_SPARSE, do not index in SPARSE mode.
Prevention
- Only SASI-index columns with reasonably distributed cardinality; use plain secondary indexes for low-cardinality flags
- Test index builds on a data sample matching production cardinality before rollout
- After version/config changes, re-verify MAX_KEYS_SPARSE has not changed
When it happens
Trigger: Calling OnDiskIndexBuilder.add() many times with the same term value (e.g. a boolean-like or low-cardinality column) in SPARSE mode, then calling build() so the term's TokenTreeBuilder exceeds MAX_KEYS_SPARSE keys when the segment is serialized.
Common situations: Indexing a low-cardinality column (status flags, booleans) where thousands of rows share one term; also happens when MAX_KEYS_SPARSE was reduced via config in custom builds and a previously-indexable column now exceeds the cap.
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
- SPARSE mode doesn't support analyzers.
- All indexed columns should be included into the column…
- case_sensitive option cannot be specified together with…
- Command ' ' took too long ( ms >= ms).
- complex columns are not yet supported by SASI
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/7aa5713fa851c36a.
Report an issue: GitHub.
Appendix: source
Thrown at src/java/org/apache/cassandra/index/sasi/disk/OnDiskIndexBuilder.java:621
private final List<TokenTreeBuilder> containers = new ArrayList<>();
private TokenTreeBuilder combinedIndex;
public MutableDataBlock(AbstractType<?> comparator, Mode mode)
{
this.comparator = comparator;
this.mode = mode;
this.combinedIndex = initCombinedIndex();
}
protected void addInternal(InMemoryDataTerm term) throws IOException
{
TokenTreeBuilder keys = term.keys;
if (mode == Mode.SPARSE)
{
if (keys.getTokenCount() > MAX_KEYS_SPARSE)
throw new IOException(String.format("Term - '%s' belongs to more than %d keys in %s mode, which is not allowed.",
comparator.getString(term.term.getBytes()), MAX_KEYS_SPARSE, mode.name()));
writeTerm(term, keys);
}
else
{
writeTerm(term, offset);
offset += keys.serializedSize();
containers.add(keys);
}
if (mode == Mode.SPARSE)
combinedIndex.add(keys);
}
protected int sizeAfter(InMemoryDataTerm element)
{View on GitHub (pinned to 88fd0f6a0e)