apache/cassandra · error · UnsupportedOperationException

You can't mix repaired and unrepaired data in a compaction

Error message

You can't mix repaired and unrepaired data in a compaction

What it means

CompactionStrategyManager.validateForCompaction enforces that a single compaction task never mixes sstables with different repair state, because repaired and unrepaired data must stay in separate sstables (required for repaired-data tracking and incremental repair). When the input collection contains both repaired (repairedAt set) and unrepaired sstables, UnsupportedOperationException is thrown. This guards user-defined compaction requests.

Source

Thrown at src/java/org/apache/cassandra/db/compaction/CompactionStrategyManager.java:1227

        }

    }

    private void validateForCompaction(Iterable<SSTableReader> input)
    {
        readLock.lock();
        try
        {
            SSTableReader firstSSTable = Iterables.getFirst(input, null);
            assert firstSSTable != null;
            boolean repaired = firstSSTable.isRepaired();
            int firstIndex = compactionStrategyIndexFor(firstSSTable);
            boolean isPending = firstSSTable.isPendingRepair();
            TimeUUID pendingRepair = firstSSTable.getSSTableMetadata().pendingRepair;
            for (SSTableReader sstable : input)
            {
                if (sstable.isRepaired() != repaired)
                    throw new UnsupportedOperationException("You can't mix repaired and unrepaired data in a compaction");
                if (firstIndex != compactionStrategyIndexFor(sstable))
                    throw new UnsupportedOperationException("You can't mix sstables from different directories in a compaction");
                if (isPending && !pendingRepair.equals(sstable.getSSTableMetadata().pendingRepair))
                    throw new UnsupportedOperationException("You can't compact sstables from different pending repair sessions");
            }
        }
        finally
        {
            readLock.unlock();
        }
    }

    public CompactionTasks getMaximalTasks(final long gcBefore, final boolean splitOutput, int permittedParallelism, OperationType operationType)
    {
        maybeReloadDiskBoundaries();
        // runWithCompactionsDisabled cancels active compactions and disables them, then we are able
        // to make the repaired/unrepaired strategies mark their own sstables as compacting. Once the
        // sstables are marked the compactions are re-enabled

View on GitHub (pinned to 88fd0f6a0e)

Solutions

  1. Partition the sstable list by isRepaired() and issue one compaction request per group
  2. Exclude repaired sstables from user-defined compaction, letting incremental compaction handle them
  3. Re-run repair first so the dataset has a consistent repair state, then compact
  4. Use nodetool compact without an explicit sstable list, which handles grouping automatically

Example fix

// before
List<SSTableReader> all = getSSTables();
cfs.getCompactionStrategyManager().getUserDefinedTasks(all, gcBefore);
// after
Map<Boolean, List<SSTableReader>> byRepaired = all.stream().collect(Collectors.partitioningBy(SSTableReader::isRepaired));
byRepaired.values().forEach(g -> cfs.getCompactionStrategyManager().getUserDefinedTasks(g, gcBefore));
Defensive patterns

Strategy: validation

Validate before calling

boolean mixed = sstables.stream().map(SSTableReader::isRepaired).distinct().count() > 1;
if (mixed) throw new IllegalArgumentException("split sstables by repaired state before user-defined compaction");

Try / catch

try { getUserDefinedTasks(input, gcBefore); }
catch (UnsupportedOperationException e) {
  // partition by isRepaired() and retry per group
}

Prevention

When it happens

Trigger: Calling getUserDefinedTasks / user-defined compaction (nodetool compact with explicit sstable list, or user-defined compaction API) passing a collection containing a mix of repaired and unrepaired SSTableReaders.

Common situations: Scripted compaction tooling that selects sstables by size/age without filtering on repairedAt; running manual compact after an incremental repair repaired only a subset of files; tooling that ignores the repaired/unrepaired split introduced with CASSANDRA-9143.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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