{"record":{"id":"9cb58188be331247","repo":"apache/cassandra","slug":"couldn-t-acquire-reference-to-the-sstable-it-m-9cb581","errorCode":null,"errorMessage":"Couldn't acquire reference to the SSTable {}. It may have been removed.","messagePattern":"Couldn't acquire reference to the SSTable (.+?)\\. It may have been removed\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/java/org/apache/cassandra/index/sai/StorageAttachedIndexBuilder.java","lineNumber":142,"sourceCode":"    private String logMessage(String message)\n    {\n        return String.format(\"[%s.%s.*] %s\", metadata.keyspace, metadata.name, message);\n    }\n\n    /**\n     * @return true if index build should be stopped\n     */\n    private boolean indexSSTable(SSTableReader sstable, Set<StorageAttachedIndex> indexes)\n    {\n        logger.debug(logMessage(\"Starting index build on {}\"), sstable.descriptor);\n\n        CountDownLatch perSSTableFileLock = null;\n        StorageAttachedIndexWriter indexWriter = null;\n\n        Ref<? extends SSTableReader> ref = sstable.tryRef();\n        if (ref == null)\n        {\n            logger.warn(logMessage(\"Couldn't acquire reference to the SSTable {}. It may have been removed.\"), sstable.descriptor);\n            return false;\n        }\n\n        try (RandomAccessReader dataFile = sstable.openDataReader();\n             LifecycleTransaction txn = LifecycleTransaction.offline(OperationType.INDEX_BUILD, sstable))\n        {\n            perSSTableFileLock = shouldWritePerSSTableFiles(sstable);\n            // If we were unable to get the per-SSTable file lock it means that the\n            // per-SSTable components are already being built, so we only want to\n            // build the per-index components\n            boolean perIndexComponentsOnly = perSSTableFileLock == null;\n            // remove existing per column index files instead of overwriting\n            IndexDescriptor indexDescriptor = IndexDescriptor.create(sstable);\n            indexes.forEach(index -> indexDescriptor.deleteColumnIndex(index.termType(), index.identifier()));\n\n            indexWriter = StorageAttachedIndexWriter.createBuilderWriter(indexDescriptor, indexes, txn, perIndexComponentsOnly);\n\n            indexWriter.begin();","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/index/sai/StorageAttachedIndexBuilder.java#L124-L160","documentation":"During SAI index builds, StorageAttachedIndexBuilder.indexSSTable attempts sstable.tryRef() to acquire a reference to the SSTableReader before opening the data file. tryRef() returns null when the SSTable has already been released/compacted away, meaning another lifecycle operation (compaction, truncation, repair cleanup) removed the SSTable while the index build was waiting in the queue. The builder logs a warning and returns false so the build can be rescheduled or skipped, rather than crashing.","triggerScenarios":"The compaction manager replaced/dropped the SSTable between the time the index build task was queued and when indexSSTable() executed; concurrent truncation (DROP TABLE / TRUNCATE) releases all SSTables while a secondary-index build is in flight; a manual `nodetool relocatesstables`/cleanup removed the reader.","commonSituations":"Running REBUILD of a storage-attached index on a busy table with heavy compaction; dropping or truncating a table while an index rebuild is queued after a node restart; repair-triggered compaction racing a newly-created index backfill.","solutions":["No action needed in most cases: the builder skips the SSTable and the build is retried against the live set; verify the index reaches a BUILD_SUCCESSFUL status via the system_views.system_storageattachedindexbuilds table.","Check the logs for the descriptor name and confirm the SSTable was compacted away (look for compaction and release logs for the same descriptor).","If the index is stuck incomplete, force a rebuild: `nodetool scsirebuild` / DROP and re-CREATE the index so all live SSTables are re-enqueued.","Reduce racing lifecycle churn (pause heavy compaction or avoid DDL on the table) during explicit index rebuilds."],"exampleFix":"// before: assuming the ref is always available\ntry (Ref<? extends SSTableReader> ref = sstable.tryRef()) { ... }\n// after: the code already handles it; callers must tolerate false\nboolean built = indexSSTable(sstable, ...);\nif (!built) logger.info(\"SSTable {} skipped for index build (already removed)\", sstable.descriptor);","handlingStrategy":"fallback","validationCode":"// before triggering index build\nif (!sstables.allMatch(s -> s.tryRef() != null)) {\n    logger.info(\"Some SSTables no longer referenceable; build set will be adjusted\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Avoid DDL (TRUNCATE/DROP) and heavy compaction while running explicit index rebuilds.","Treat a `false` return from indexSSTable as a normal skip, not a failure.","Monitor index build status tables to confirm the skipped SSTable set gets rebuilt."],"tags":["sstable","index-build","race-condition","compaction"],"backgroundTag":"resource-not-found","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"}