{"record":{"id":"4df03b91345d9dcc","repo":"apache/cassandra","slug":"failed-setting-pending-repair-to-s-on-s-pending","errorCode":null,"errorMessage":"Failed setting pending repair to %s on %s (pending repair is %s)","messagePattern":"Failed setting pending repair to (.+?) on (.+?) \\(pending repair is (.+?)\\)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/db/compaction/CompactionStrategyManager.java","lineNumber":1533,"sourceCode":"        finally\n        {\n            try\n            {\n                // if there was an exception mutating repairedAt, we should still notify for the\n                // sstables that we were able to modify successfully before releasing the lock\n                cfs.getTracker().notifySSTableRepairedStatusChanged(changed);\n            }\n            finally\n            {\n                writeLock.unlock();\n            }\n        }\n    }\n\n    private static void verifyMetadata(SSTableReader sstable, long repairedAt, TimeUUID pendingRepair, boolean isTransient)\n    {\n        if (!Objects.equals(pendingRepair, sstable.getPendingRepair()))\n            throw new IllegalStateException(String.format(\"Failed setting pending repair to %s on %s (pending repair is %s)\", pendingRepair, sstable, sstable.getPendingRepair()));\n        if (repairedAt != sstable.getRepairedAt())\n            throw new IllegalStateException(String.format(\"Failed setting repairedAt to %d on %s (repairedAt is %d)\", repairedAt, sstable, sstable.getRepairedAt()));\n        if (isTransient != sstable.isTransient())\n            throw new IllegalStateException(String.format(\"Failed setting isTransient to %b on %s (isTransient is %b)\", isTransient, sstable, sstable.isTransient()));\n    }\n\n    public CleanupSummary releaseRepairData(Collection<TimeUUID> sessions)\n    {\n        List<CleanupTask> cleanupTasks = new ArrayList<>();\n        readLock.lock();\n        try\n        {\n            for (PendingRepairManager prm : Iterables.concat(pendingRepairs.getManagers(), transientRepairs.getManagers()))\n                cleanupTasks.add(prm.releaseSessionData(sessions));\n        }\n        finally\n        {\n            readLock.unlock();","sourceCodeStart":1515,"sourceCodeEnd":1551,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/db/compaction/CompactionStrategyManager.java#L1515-L1551","documentation":"verifyMetadata is the post-condition check after CompactionStrategyManager mutates an sstable's repair metadata (via mutateRepaired, used by repair/anti-compaction to set pendingRepair, repairedAt, and isTransient). It re-reads the live values and throws IllegalStateException if the pendingRepair id written does not match what was requested - i.e. the metadata mutation silently failed or raced with another mutation.","triggerScenarios":"Calling sstable.mutateRepaired/setPendingRepair during incremental repair completion or anticompaction, then verifying; a concurrent metadata mutation (another repair transition, compaction finishing, or sstable being replaced) overwrote pendingRepair between write and read.","commonSituations":"Concurrent incremental repair sessions touching the same sstable; repair finishing while compaction claims the sstable; races with sstable replacement/mark-compacting during cleanup or repair-finalization; retry storms in repair finalization.","solutions":["Re-run repair on the affected ranges - the failed metadata update will be retried safely","Retry repair finalization (repair_admin operations) after transient concurrent activity settles","Avoid running concurrent repairs or manual compactions on the same table during repair finalization","Check logs for which mutation raced; if reproducible without concurrency, file a Cassandra bug"],"exampleFix":"// before\nsstable.mutateRepaired(ActiveRepairService.UNREPAIRED_SSTABLE, session, false);\n// after\ntry { sstable.mutateRepaired(ActiveRepairService.UNREPAIRED_SSTABLE, session, false); }\ncatch (IllegalStateException e) { logger.warn(\"metadata race on {}, will be retried by repair\", sstable); }","handlingStrategy":"try-catch","validationCode":"// ensure no concurrent repair/compaction before mutating metadata\nif (!CompactionManager.instance.getCompactions().isEmpty() || repairInFlight(cfs))\n    throw new IllegalStateException(\"defer metadata mutation until repair/compaction quiesce\");","typeGuard":null,"tryCatchPattern":"try { sstable.mutateRepairedAt(...); verifyMetadata(sstable, repairedAt, pendingRepair, isTransient); }\ncatch (IllegalStateException e) {\n  logger.warn(\"repair metadata race on {}\", sstable, e);\n  // allow repair to retry the finalization\n}","preventionTips":["Serialize repair finalization per table","Do not run user-defined compaction while repair finalizes","Verify metadata only under the sstable's lifecycle lock","Monitor logs for repeated verifyMetadata failures - they indicate real races"],"tags":["repair","race-condition","sstable-metadata","internal-error"],"backgroundTag":"invalid-state-transition","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"}