{"record":{"id":"40df47a3e5a4d272","repo":"apache/cassandra","slug":"can-t-move-and-open-a-file-that-is-already-in-use","errorCode":null,"errorMessage":"Can't move and open a file that is already in use in the table %s -> %s","messagePattern":"Can't move and open a file that is already in use in the table (.+?) -> (.+?)","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java","lineNumber":1879,"sourceCode":"    /**\n     * Moves the sstable in oldDescriptor to a new place (with generation etc) in newDescriptor.\n     * <p>\n     * All components given will be moved/renamed\n     */\n    public static SSTableReader moveAndOpenSSTable(ColumnFamilyStore cfs, Descriptor oldDescriptor, Descriptor newDescriptor, Set<Component> components, boolean copyData)\n    {\n        if (!oldDescriptor.isCompatible())\n            throw new RuntimeException(String.format(\"Can't open incompatible SSTable! Current version %s, found file: %s\",\n                                                     oldDescriptor.getFormat().getLatestVersion(),\n                                                     oldDescriptor));\n\n        boolean isLive = cfs.getLiveSSTables().stream().anyMatch(r -> r.descriptor.equals(newDescriptor)\n                                                                      || r.descriptor.equals(oldDescriptor));\n        if (isLive)\n        {\n            String message = String.format(\"Can't move and open a file that is already in use in the table %s -> %s\", oldDescriptor, newDescriptor);\n            logger.error(message);\n            throw new RuntimeException(message);\n        }\n        if (newDescriptor.fileFor(Components.DATA).exists())\n        {\n            String msg = String.format(\"File %s already exists, can't move the file there\", newDescriptor.fileFor(Components.DATA));\n            logger.error(msg);\n            throw new RuntimeException(msg);\n        }\n\n        if (copyData)\n        {\n            try\n            {\n                logger.info(\"Hardlinking new SSTable {} to {}\", oldDescriptor, newDescriptor);\n                hardlink(oldDescriptor, newDescriptor, components);\n            }\n            catch (FSWriteError ex)\n            {\n                logger.warn(\"Unable to hardlink new SSTable {} to {}, falling back to copying\", oldDescriptor, newDescriptor, ex);","sourceCodeStart":1861,"sourceCodeEnd":1897,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java#L1861-L1897","documentation":"moveAndOpenSSTable() checks the live SSTable set of the column family store and refuses to move/open a descriptor that is already tracked as live (either the old or the new descriptor equals a live reader's descriptor). This protects against double-ownership of an SSTable file, which would corrupt state if the same data file were moved under an active reader.","triggerScenarios":"Calling moveAndOpenSSTable with an oldDescriptor or newDescriptor that matches a descriptor in cfs.getLiveSSTables() — e.g. re-invoking an import/relocation job for an SSTable that was already opened, or racing concurrent move operations on the same file.","commonSituations":"Re-running sstable import/relocation scripts twice; concurrent bulk-load operations targeting the same table; offline tools run against a live (not stopped) node.","solutions":["Ensure the SSTable is not already loaded: check nodetool sstableinfo / table metrics before moving","Run the move/import operation only once per descriptor; make jobs idempotent by checking existence first","Stop the node or use offline tools only against a stopped instance to avoid racing live readers","Skip descriptors already present in the live set in your tooling before calling moveAndOpenSSTable"],"exampleFix":"// before: unconditional move\nSSTableReader.moveAndOpenSSTable(cfs, oldDesc, newDesc, comps, false);\n// after: skip if already live\nboolean live = cfs.getLiveSSTables().stream()\n    .anyMatch(r -> r.descriptor.equals(oldDesc) || r.descriptor.equals(newDesc));\nif (!live) SSTableReader.moveAndOpenSSTable(cfs, oldDesc, newDesc, comps, false);","handlingStrategy":"validation","validationCode":"// skip if already loaded\nboolean live = cfs.getLiveSSTables().stream()\n    .anyMatch(r -> r.descriptor.equals(oldDesc) || r.descriptor.equals(newDesc));\nif (live) { skip(); }","typeGuard":null,"tryCatchPattern":"try { SSTableReader.moveAndOpenSSTable(cfs, oldDesc, newDesc, comps, false); }\ncatch (RuntimeException e) {\n    if (e.getMessage().startsWith(\"Can't move and open a file that is already in use\")) {\n        logger.warn(\"Skipping already-live sstable {}\", oldDesc); // idempotent skip\n    } else throw e;\n}","preventionTips":["Make import/move jobs idempotent by checking live descriptors first","Never run offline SSTable tools against a live node","Serialize move/import operations per table to avoid races","Track processed descriptors in job state to avoid re-processing"],"tags":["sstable","state-conflict","concurrency"],"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-17T15:17:12.973Z"}