apache/cassandra · critical · IllegalStateException
Received a command with the thrift flag set. This means…
Error message
Received a command with the thrift flag set. This means thrift is in use in a mixed 3.0/3.X and 4.0+ cluster, which is unsupported. Make sure to stop using thrift before upgrading to 4.0
What it means
ReadCommand's serializer for messages from pre-4.0 (3.0/3.X) nodes throws IllegalStateException when the deserialized command has the thrift 'forThrift' flag set. Thrift was removed in 4.0; seeing this flag means an unsupported mixed-version cluster is still issuing Thrift-originated reads, and proceeding would silently do the wrong thing.
Solutions
- Stop all Thrift clients and migrate them to CQL (the 4.0+ native protocol) before continuing the rolling upgrade.
- Verify no nodes/clients use thrift: check start_rpc settings and application configs on 3.x nodes.
- Complete the cluster upgrade so all nodes are 4.0+ (which rejects thrift entirely), keeping versions homogeneous.
- If seen unexpectedly, check for stale 3.x nodes still joined to the cluster (nodetool status / describecluster).
Example fix
// before // legacy app on 3.x node using thrift client against mixed cluster cassandra.client.transport=THRIFT // after cqlSession = CqlSession.builder().addContactPoint(node).build(); // CQL 4 native protocol
Defensive patterns
Strategy: fallback
Validate before calling
// Before upgrading: audit all clients for thrift usage. // On 3.x: grep start_rpc in cassandra.yaml; check netstat for port 9160 connections.
Try / catch
try { deserialize(cmd); } catch (IllegalStateException e) {
if (e.getMessage().contains("thrift flag")) { alertOpsMixedVersionCluster(); haltUpgrade(); } else throw e;
} Prevention
- Migrate all Thrift clients to CQL before starting any 3.x → 4.x upgrade.
- Keep cluster versions homogeneous during rolling upgrades.
- Check port 9160 (thrift) traffic during upgrades.
- Document the upgrade prerequisite: thrift must be retired first.
When it happens
Trigger: Rolling upgrade of a 3.0/3.X cluster to 4.0+ while some application or node still uses the Thrift protocol; an old node serializing a read command flagged for Thrift that a 4.0+ node tries to deserialize.
Common situations: Mixed 3.x/4.0 clusters during upgrade where legacy Thrift clients were never migrated to CQL; forgotten legacy applications using the old thrift API; upgrade runbooks that skipped the 'stop using thrift' prerequisite.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Aggregate function cannot be used for masking table columns
- All nodes are not yet upgraded
- Altering field types is no longer supported
- Bad NodeState
- Can't ignore local host
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/781e2e08d0066bd6.
Report an issue: GitHub.
Appendix: source
Thrown at src/java/org/apache/cassandra/db/ReadCommand.java:1465
if (hasIndex)
{
IndexMetadata index = deserializeIndexMetadata(in, version, tableMetadata);
Index.Group indexGroup = Keyspace.openAndGetStore(tableMetadata).indexManager.getIndexGroup(index);
if (indexGroup != null)
indexQueryPlan = indexGroup.queryPlanFor(rowFilter);
}
return deserializer.deserialize(in, version, schemaVersion, isDigest, digestVersion, acceptsTransient, potentialTxnConflicts, tableMetadata, nowInSec, columnFilter, rowFilter, limits, indexQueryPlan);
}
public ReadCommand deserialize(DataInputPlus in, int version) throws IOException
{
Kind kind = Kind.fromOrdinal(in.readByte());
int flags = in.readByte();
// Shouldn't happen or it's a user error (see comment above) but
// better complain loudly than doing the wrong thing.
if (isForThrift(flags))
throw new IllegalStateException("Received a command with the thrift flag set. "
+ "This means thrift is in use in a mixed 3.0/3.X and 4.0+ cluster, "
+ "which is unsupported. Make sure to stop using thrift before "
+ "upgrading to 4.0");
int digestVersion = isDigest(flags) ? in.readUnsignedVInt32() : 0;
TableId tableId = TableId.deserialize(in);
Epoch schemaVersion = Epoch.EMPTY;
if (version >= MessagingService.VERSION_60)
schemaVersion = Epoch.serializer.deserialize(in);
TableMetadata tableMetadata;
try
{
tableMetadata = schema.getExistingTableMetadata(tableId);
}
catch (UnknownTableException e)
{
ClusterMetadata metadata = ClusterMetadata.current();View on GitHub (pinned to 88fd0f6a0e)