{"record":{"id":"4419fa448f5116a0","repo":"apache/cassandra","slug":"failed-to-acquire-reference-to-compression-diction","errorCode":null,"errorMessage":"Failed to acquire reference to compression dictionary","messagePattern":"Failed to acquire reference to compression dictionary","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/io/compress/CompressionMetadata.java","lineNumber":171,"sourceCode":"        this.compressedFileLength = compressedFileLength;\n        this.chunkOffsets = chunkOffsets;\n        this.chunkOffsetsSize = chunkOffsetsSize;\n        this.compressionDictionary = compressionDictionary;\n    }\n\n    private static AutoCloseable[] buildCloseableArray(Memory chunkOffsets, CompressionDictionary dictionary)\n    {\n        if (dictionary == null)\n            return new AutoCloseable[] { chunkOffsets };\n\n        Ref<? extends CompressionDictionary> dictRef = dictionary.tryRef();\n        if (dictRef == null)\n        {\n            // Close chunkOffsets before throwing to prevent resource leak.\n            // The CompressionMetadata constructor will not complete if we throw here,\n            // so we must clean up resources that were passed in.\n            chunkOffsets.close();\n            throw new IllegalStateException(\"Failed to acquire reference to compression dictionary\");\n        }\n\n        return new AutoCloseable[] { chunkOffsets, dictRef::release };\n    }\n\n    /**\n     * Copy constructor for creating shared copies via sharedCopy().\n     * <br>\n     * This uses the WrappedSharedCloseable pattern where all copies share the same\n     * underlying resources (chunkOffsets Memory and dictionary reference). The super()\n     * call increments the shared reference count, and resources are only released when\n     * the last copy is closed.\n     * <br>\n     * Reference counting behavior:\n     * - Original CompressionMetadata acquires 1 dictionary reference (in buildCloseableArray)\n     * - All copies share that reference (via super(copy) incrementing shared ref count)\n     * - When last copy closes, WrappedSharedCloseable.Tidy releases the reference once\n     */","sourceCodeStart":153,"sourceCodeEnd":189,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/io/compress/CompressionMetadata.java#L153-L189","documentation":"When constructing CompressionMetadata for a table using compression dictionaries, buildCloseableArray() must acquire a reference to the dictionary via the dictionary manager. If the ref-counted acquisition returns null (dictionary unavailable/already evicted), the constructor cannot proceed; it closes the chunkOffsets memory first to avoid a leak and throws IllegalStateException.","triggerScenarios":"Creating CompressionMetadata via the constructor when CompressionDictionaryManager cannot supply an active reference for the dictionary id stored in the metadata — e.g. dictionary file removed, cache shut down, or ref-count lifecycle already released.","commonSituations":"Dictionary files deleted while SSTables referencing them remain; shutdown race where metadata is opened after dictionary manager closes; mismatched/moved dictionary storage after restoring from backup without the dictionary directory.","solutions":["Restore/keep the compression dictionary files alongside the SSTables when copying or restoring data","Check logs for dictionary manager/eviction errors and restart the node so dictionaries reload","Rewrite the SSTables without dictionaries (disable compression dictionaries for the table, then scrub/upgrade-sstables) if dictionaries are unrecoverable","If constructing metadata in code, close chunkOffsets on failure — or prefer the factory open() paths that handle teardown"],"exampleFix":"// before\nCompressionMetadata m = new CompressionMetadata(file, len, useMmap); // throws if dict ref null\n// after\ntry {\n    CompressionMetadata m = new CompressionMetadata(file, len, useMmap);\n} catch (IllegalStateException e) {\n    logger.error(\"Dictionary unavailable for {}\", file, e);\n    restoreDictionariesOrRewriteSSTable(file);\n}","handlingStrategy":"try-catch","validationCode":"// Ensure dictionary exists before constructing metadata\nif (dictionaryManager != null && !dictionaryManager.contains(dictId))\n    throw new IllegalStateException(\"Dictionary missing: \" + dictId);","typeGuard":null,"tryCatchPattern":"try { return new CompressionMetadata(file, len, useMmap); }\ncatch (IllegalStateException e) { closeQuietly(chunkOffsets); restoreDictionaryOrRewrite(); throw e; }","preventionTips":["Ship dictionary files with the SSTables they reference","Disable compression dictionaries before removing dictionary files","Test restore procedures to include dictionary directories","Release dict refs promptly to avoid ref-count exhaustion"],"tags":["compression-dictionary","reference-counting","sstable","lifecycle"],"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-14T16:17:12.679Z"}