{"record":{"id":"a22a4bda9a7e0f9f","repo":"apache/cassandra","slug":"you-can-t-compact-sstables-from-different-pending","errorCode":null,"errorMessage":"You can't compact sstables from different pending repair sessions","messagePattern":"You can't compact sstables from different pending repair sessions","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/db/compaction/CompactionStrategyManager.java","lineNumber":1231,"sourceCode":"    private void validateForCompaction(Iterable<SSTableReader> input)\n    {\n        readLock.lock();\n        try\n        {\n            SSTableReader firstSSTable = Iterables.getFirst(input, null);\n            assert firstSSTable != null;\n            boolean repaired = firstSSTable.isRepaired();\n            int firstIndex = compactionStrategyIndexFor(firstSSTable);\n            boolean isPending = firstSSTable.isPendingRepair();\n            TimeUUID pendingRepair = firstSSTable.getSSTableMetadata().pendingRepair;\n            for (SSTableReader sstable : input)\n            {\n                if (sstable.isRepaired() != repaired)\n                    throw new UnsupportedOperationException(\"You can't mix repaired and unrepaired data in a compaction\");\n                if (firstIndex != compactionStrategyIndexFor(sstable))\n                    throw new UnsupportedOperationException(\"You can't mix sstables from different directories in a compaction\");\n                if (isPending && !pendingRepair.equals(sstable.getSSTableMetadata().pendingRepair))\n                    throw new UnsupportedOperationException(\"You can't compact sstables from different pending repair sessions\");\n            }\n        }\n        finally\n        {\n            readLock.unlock();\n        }\n    }\n\n    public CompactionTasks getMaximalTasks(final long gcBefore, final boolean splitOutput, int permittedParallelism, OperationType operationType)\n    {\n        maybeReloadDiskBoundaries();\n        // runWithCompactionsDisabled cancels active compactions and disables them, then we are able\n        // to make the repaired/unrepaired strategies mark their own sstables as compacting. Once the\n        // sstables are marked the compactions are re-enabled\n        return cfs.runWithCompactionsDisabled(() -> {\n            List<AbstractCompactionTask> tasks = new ArrayList<>();\n            readLock.lock();\n            try","sourceCodeStart":1213,"sourceCodeEnd":1249,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/db/compaction/CompactionStrategyManager.java#L1213-L1249","documentation":"When sstables are pending an incremental repair (isPendingRepair), each belongs to one pending repair session identified by its pendingRepair TimeUUID. validateForCompaction throws UnsupportedOperationException if a compaction input mixes sstables from different pending-repair sessions, because compacting them would merge anti-compacted data belonging to distinct repair sessions and corrupt repair bookkeeping.","triggerScenarios":"User-defined compaction selecting pending-repair sstables from more than one incremental repair session (different pendingRepair ids) while the first sstable isPending - checked via sstable.getSSTableMetadata().pendingRepair inequality.","commonSituations":"Running concurrent incremental repairs on the same table then attempting manual compaction of pending sstables; repair sessions abandoned without release; repair-adjacent tooling that collects all pending sstables into one compact call.","solutions":["Wait for the incremental repairs to finish (or release the sessions) so pending sstables are transitioned out of pending state, then compact","Group sstables by pendingRepair id and issue one compaction per session (only while the session is still active is even that restricted)","Use repair_admin to cancel/abort stale sessions, then nodetool garbagcollect or restart cleanup to clear pending markers","Avoid user-defined compaction of pending sstables entirely; let repair finish first"],"exampleFix":"// before\ngetUserDefinedTasks(pendingSstables, gcBefore); // multiple sessions\n// after\nMap<TimeUUID, List<SSTableReader>> bySession = pendingSstables.stream().collect(Collectors.groupingBy(s -> s.getSSTableMetadata().pendingRepair));\nbySession.values().forEach(g -> getUserDefinedTasks(g, gcBefore));","handlingStrategy":"validation","validationCode":"boolean multipleSessions = sstables.stream()\n    .filter(SSTableReader::isPendingRepair)\n    .map(s -> s.getSSTableMetadata().pendingRepair).distinct().count() > 1;\nif (multipleSessions) throw new IllegalArgumentException(\"one compaction per pending repair session\");","typeGuard":null,"tryCatchPattern":"try { getUserDefinedTasks(input, gcBefore); }\ncatch (UnsupportedOperationException e) {\n  // wait for repair sessions to finish or group by pendingRepair id and retry\n}","preventionTips":["Never manually compact pending-repair sstables; wait for incremental repair to complete","Group inputs by SSTableMetadata.pendingRepair when scripting repair tooling","Avoid concurrent incremental repairs on the same table","Use repair_admin to clean up abandoned sessions before compaction"],"tags":["compaction","repair","incremental-repair","unsupported-operation"],"backgroundTag":"unsupported-operation","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}