{"record":{"id":"274cb30868fb995f","repo":"apache/cassandra","slug":"you-can-t-mix-repaired-and-unrepaired-data-in-a-co","errorCode":null,"errorMessage":"You can't mix repaired and unrepaired data in a compaction","messagePattern":"You can't mix repaired and unrepaired data in a compaction","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/db/compaction/CompactionStrategyManager.java","lineNumber":1227,"sourceCode":"        }\n\n    }\n\n    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","sourceCodeStart":1209,"sourceCodeEnd":1245,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/db/compaction/CompactionStrategyManager.java#L1209-L1245","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Partition the sstable list by isRepaired() and issue one compaction request per group","Exclude repaired sstables from user-defined compaction, letting incremental compaction handle them","Re-run repair first so the dataset has a consistent repair state, then compact","Use nodetool compact without an explicit sstable list, which handles grouping automatically"],"exampleFix":"// before\nList<SSTableReader> all = getSSTables();\ncfs.getCompactionStrategyManager().getUserDefinedTasks(all, gcBefore);\n// after\nMap<Boolean, List<SSTableReader>> byRepaired = all.stream().collect(Collectors.partitioningBy(SSTableReader::isRepaired));\nbyRepaired.values().forEach(g -> cfs.getCompactionStrategyManager().getUserDefinedTasks(g, gcBefore));","handlingStrategy":"validation","validationCode":"boolean mixed = sstables.stream().map(SSTableReader::isRepaired).distinct().count() > 1;\nif (mixed) throw new IllegalArgumentException(\"split sstables by repaired state before user-defined compaction\");","typeGuard":null,"tryCatchPattern":"try { getUserDefinedTasks(input, gcBefore); }\ncatch (UnsupportedOperationException e) {\n  // partition by isRepaired() and retry per group\n}","preventionTips":["Always partition user-compaction inputs on SSTableReader.isRepaired()","Prefer full-table nodetool compact over hand-picked sstable lists","Run repair before manual compaction so repair state is uniform","Educate ops scripts about the repaired/unrepaired split"],"tags":["compaction","repair","unsupported-operation","sstable"],"backgroundTag":"unsupported-operation","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}