{"record":{"id":"a20be5b100a07060","repo":"apache/cassandra","slug":"cache-schema-version-expected-does-not-match-c","errorCode":null,"errorMessage":"Cache schema version + expected + does not match current schema version + actual","messagePattern":"Cache schema version \\+ expected \\+ does not match current schema version \\+ actual","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"warning","filePath":"src/java/org/apache/cassandra/cache/AutoSavingCache.java","lineNumber":227,"sourceCode":"        File metadataPath = getCacheMetadataPath(CURRENT_VERSION);\n        if (dataPath.exists() && crcPath.exists() && metadataPath.exists())\n        {\n            DataInputStreamPlus in = null;\n            try\n            {\n                logger.info(\"Reading saved cache: {}, {}, {}\", dataPath, crcPath, metadataPath);\n                try (FileInputStreamPlus metadataIn = metadataPath.newInputStream())\n                {\n                    cacheLoader.deserializeMetadata(metadataIn);\n                }\n\n                in = streamFactory.getInputStream(dataPath, crcPath);\n\n                //Check the schema has not changed since CFs are looked up by name which is ambiguous\n                UUID expected = new UUID(in.readLong(), in.readLong());\n                UUID actual = ClusterMetadata.current().schema.getVersion();\n                if (!expected.equals(actual))\n                    throw new RuntimeException(\"Cache schema version \"\n                                               + expected\n                                               + \" does not match current schema version \"\n                                               + actual);\n\n                ArrayDeque<Future<Pair<K, V>>> futures = new ArrayDeque<>();\n                long loadByNanos = start + TimeUnit.SECONDS.toNanos(DatabaseDescriptor.getCacheLoadTimeout());\n                while (nanoTime() < loadByNanos && in.available() > 0)\n                {\n                    Future<Pair<K, V>> entryFuture = cacheLoader.deserialize(in);\n                    // Key cache entry can return null, if the SSTable doesn't exist.\n                    if (entryFuture == null)\n                        continue;\n\n                    futures.offer(entryFuture);\n                    count++;\n\n                    /*\n                     * Kind of unwise to accrue an unbounded number of pending futures","sourceCodeStart":209,"sourceCodeEnd":245,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/cache/AutoSavingCache.java#L209-L245","documentation":"AutoSavingCache.loadSaved reads a saved key/key-cache file whose header records the schema version UUID at the time the cache was persisted. On startup, the loader compares that UUID against ClusterMetadata.current().schema.getVersion(); if they differ, it throws this RuntimeException. The check exists because cache entries reference column families by name, which is ambiguous across schema versions — a stale cache could silently map keys to the wrong table.","triggerScenarios":"A node restarts with auto-saved cache files in the saved_caches directory that were written under a previous schema (any DDL change — CREATE/ALTER/DROP TABLE — made since the cache was saved, or cache files left over from a rollback/restore). loadSaved reads the header UUID and it no longer equals the current schema version.","commonSituations":"Operator changes schema (adds a table or column) and immediately restarts while caches_keys/saved_caches still hold pre-change files; restoring a node from a backup taken with an older schema; failed upgrade/rollback leaves stale cache files on disk.","solutions":["This is normally harmless and self-healing: acknowledge the RuntimeException in the log, and note Cassandra simply skips loading the stale cache (fresh entries rebuild on demand).","If it recurs or blocks startup, stop the node and delete the stale files in the saved_caches directory (e.g. Keyspace-Table-KeyCache-*), then restart.","Avoid restoring cache files from backups taken under a different schema; restore data and let caches rebuild.","Perform schema migrations before/with restarts so cache snapshots and schema stay in sync where possible."],"exampleFix":"// no code fix; remediation is clearing the stale cache on disk:\n// before: saved_caches/Keyspace1-Standard1-KeyCache-<old-schema-uuid>.db loaded at startup -> RuntimeException\n// after:  rm saved_caches/*KeyCache*  (or let Cassandra discard it), restart, cache rebuilds\n","handlingStrategy":"validation","validationCode":"UUID current = ClusterMetadata.current().schema.getVersion();\njava.io.File cacheFile = new java.io.File(DatabaseDescriptor.getSavedCachesLocation(), keyspace + \"-\" + table + \"-KeyCache-\");\n// if saved cache files predate the latest schema change (e.g. timestamp < last DDL time), skip loading and let the cache rebuild","typeGuard":null,"tryCatchPattern":"try { cache.loadSavedAsync(); }\ncatch (RuntimeException e) { if (e.getMessage() != null && e.getMessage().contains(\"does not match current schema version\")) logger.warn(\"Skipping stale cache load; schema changed since cache was saved; rebuilding\"); else throw e; }","preventionTips":["Take cache snapshots and schema changes together; don't restore saved_caches from backups of a different schema generation.","After any DDL change followed by restart, expect cache-load skips and rebuild; monitor for the warning.","Clean saved_caches when restoring nodes from backups or rolling back upgrades."],"tags":["cache","schema-version","startup","stale-data"],"backgroundTag":"schema-validation-failed","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"}