{"record":{"id":"0620464c04595c3b","repo":"apache/cassandra","slug":"can-t-route-consensus-request-to-nonexistent-cfs","errorCode":null,"errorMessage":"Can't route consensus request to nonexistent CFS %s.%s","messagePattern":"Can't route consensus request to nonexistent CFS (.+?)\\.(.+?)","errorType":"exception","errorClass":"InvalidRequestException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/service/consensus/migration/ConsensusRequestRouter.java","lineNumber":244,"sourceCode":"\n        if (tms.migratingRanges.intersects(token))\n            return pickBasedOnKeyMigrationStatus(cm, tmd, tms, key, consistencyLevel, requestTime, timeoutNanos, isForWrite);\n\n        // It's not migrated so infer the protocol from the target\n        return pickNotMigrated(tms.targetProtocol);\n    }\n\n    /**\n     * If the key was already migrated then we can pick the target protocol otherwise\n     * we have to run a repair operation on the key to migrate it.\n     */\n    private static ConsensusRoutingDecision pickBasedOnKeyMigrationStatus(ClusterMetadata cm, TableMetadata tmd, TableMigrationState tms, DecoratedKey key, ConsistencyLevel consistencyLevel, Dispatcher.RequestTime requestTime, long timeoutNanos, boolean isForWrite)\n    {\n        checkState(pickPaxos() != ConsensusRoutingDecision.PAXOSV1, \"Can't migrate from PaxosV1 to anything\");\n\n        ColumnFamilyStore cfs = ColumnFamilyStore.getIfExists(tmd.id);\n        if (cfs == null)\n            throw new InvalidRequestException(\"Can't route consensus request to nonexistent CFS %s.%s\".format(tmd.keyspace, tmd.name));\n\n        // Migration to accord has two phases for each range, in the first phase we can't do key migration because Accord\n        // can't safely read until the range has had its data repaired so Paxos continues to be used for all reads\n        // and writes\n        Token token = key.getToken();\n        if (tms.targetProtocol == ConsensusMigrationTarget.accord && tms.repairPendingRanges.intersects(token))\n            return pickPaxos();\n\n        // If it is locally replicated we can check our local migration state to see if it was already migrated\n        EndpointsForToken naturalReplicas = ReplicaLayout.forNonLocalStrategyTokenRead(cm, cfs.keyspace.getMetadata(), token);\n        boolean isLocallyReplicated = naturalReplicas.lookup(FBUtilities.getBroadcastAddressAndPort()) != null;\n        if (isLocallyReplicated)\n        {\n            ConsensusMigratedAt consensusMigratedAt = getConsensusMigratedAt(tms.tableId, key);\n            // Check that key migration that was performed satisfies the requirements of the current in flight migration\n            // for the range\n            // Be aware that for Accord->Paxos the cache only tells us if the key was repaired locally\n            // This ends up still being safe because every single Paxos read (in a migrating range) during migration will check","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/service/consensus/migration/ConsensusRequestRouter.java#L226-L262","documentation":"During Paxos-to-Accord consensus migration, the router must resolve the table's ColumnFamilyStore (CFS) instance to route a consensus request. If the CFS cannot be found by table ID at routing time, the table has been dropped or is not yet/ no longer present in this node's local schema, and Cassandra throws this InvalidRequestException rather than route to a nonexistent store.","triggerScenarios":"A client sends a serial/consensus (LWT or transactional) read or write to a table whose schema metadata still exists in ClusterMetadata but whose local ColumnFamilyStore lookup (ColumnFamilyStore.getIfExists(tmd.id)) returns null — typically the table was dropped concurrently, or the node's local CFS has not been created/not yet initialized for that table.","commonSituations":"Race between dropping a table and in-flight LWT/transactional requests hitting it; schema disagreement where one node knows the drop and another still routes; requests arriving during node startup before all CFS instances are constructed; stale prepared statements referencing a dropped table.","solutions":["Check that the table still exists (system_schema.tables / DESCRIBE) and re-create it if it was dropped unintentionally","Retry the operation on a different node or after schema agreement is reached (nodetool checkschemaversions)","Clear stale client state: re-prepare statements against the live table","If it occurs during rolling restarts, ensure the node has fully completed startup and CFS construction before serving traffic"],"exampleFix":"// before: blind retry loop on InvalidRequestException\nsession.execute(lwtInsert);\n// after: guard against dropped table\ntry {\n    session.execute(lwtInsert);\n} catch (InvalidRequestException e) {\n    if (schema.tableExists(keyspace, table)) throw e;\n    // table was dropped; skip or recreate\n}","handlingStrategy":"validation","validationCode":"Row row = session.execute(\"SELECT table_name FROM system_schema.tables WHERE keyspace_name=? AND table_name=?\", ks, table).one();\nif (row == null) throw new IllegalStateException(\"Table \" + ks + \".\" + table + \" does not exist\");","typeGuard":null,"tryCatchPattern":"try { session.execute(lwt); } catch (InvalidRequestException e) { /* table likely dropped; refresh schema metadata and re-check */ session.refreshSchema(); }","preventionTips":["Check table existence before issuing LWTs after DDL changes","Wait for schema agreement after CREATE/DROP TABLE before sending traffic","Avoid racing DROP TABLE with in-flight transactions"],"tags":["consensus-migration","invalid-request","table-not-found","schema"],"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"}