apache/cassandra · error · RuntimeException
Bad NodeState
Error message
Bad NodeState
What it means
GossipHelper.nodeStateToStatus converts legacy gossip NodeState values into VersionedValue statuses during gossip-to-TCM upgrade; the default branch throws RuntimeException because a NodeState outside the handled set cannot be represented, indicating an unanticipated state during upgrade.
Source
Thrown at src/java/org/apache/cassandra/tcm/compatibility/GossipHelper.java:186
break;
case MOVING:
sequence = metadata.inProgressSequences.get(nodeId);
if (!(sequence instanceof Move))
{
logger.error(String.format("Cannot construct gossip state. Node is in %s state, but sequence the is %s", NodeState.MOVING, sequence));
return null;
}
Collection<Token> moveTokens = getTokensFromOperation(sequence);
if (!moveTokens.isEmpty())
{
Token token = ((Move) sequence).tokens.iterator().next();
status = valueFactory.moving(token);
}
break;
case REGISTERED:
break;
default:
throw new RuntimeException("Bad NodeState " + nodeState);
}
return status;
}
public static Collection<Token> getTokensFromOperation(NodeId nodeId, ClusterMetadata metadata)
{
return getTokensFromOperation(metadata.inProgressSequences.get(nodeId));
}
public static Collection<Token> getTokensFromOperation(MultiStepOperation<?> sequence)
{
if (null == sequence)
return Collections.emptySet();
if (sequence.kind() == MultiStepOperation.Kind.JOIN)
return new HashSet<>(((BootstrapAndJoin)sequence).finishJoin.tokens);
else if (sequence.kind() == MultiStepOperation.Kind.REPLACE)
return new HashSet<>(((BootstrapAndReplace)sequence).bootstrapTokens);View on GitHub (pinned to 88fd0f6a0e)
Solutions
- Upgrade all nodes so every member understands each other's NodeState values
- Check which node/state triggered it in logs; if it's a leftover dead node, remove it from the cluster before the TCM migration
- Ensure the pre-upgrade Cassandra version is a supported migration baseline
- Report/patch: extend nodeStateToStatus with the missing NodeState case if it's a legitimate state
Example fix
// before
default:
throw new RuntimeException("Bad NodeState " + nodeState);
// after
case UNKNOWN_NEW_STATE: // e.g. added in a later version
status = valueFactory.normal(Collections.emptySet());
break;
default:
throw new RuntimeException("Bad NodeState " + nodeState); Defensive patterns
Strategy: try-catch
Validate before calling
// pre-upgrade check
for (EndpointState es : gossipStates)
assert knownNodeStates.contains(es.getNodeState()) : "unknown NodeState during upgrade"; Try / catch
try { GossipHelper.nodeStateToStatus(nodeState, valueFactory); }
catch (RuntimeException e) {
if (e.getMessage().startsWith("Bad NodeState")) { /* upgrade version or remove stale node */ }
else throw e;
} Prevention
- Upgrade to a uniform gossip-compat version across the cluster before TCM migration
- Purge dead/stale endpoints before upgrade
- Avoid mixed-version upgrades spanning unsupported baselines
When it happens
Trigger: Running the gossip compatibility/upgrade path with an endpoint whose NodeState is not one of the explicitly handled values (e.g. LEFT-ish, future/unknown enum value) when translating to a gossip status.
Common situations: Mixed-version upgrade where a newer node advertises a NodeState this node's code doesn't know; corrupted endpoint state; attempting upgrade with an unsupported intermediate version.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- Can't upgrade the first node when STATUS =
- Extracting tokens from %s sequence is neither necessary nor
- Unknown host in epState for
- Invalid endpoint state for {}; {} - {}
- The index %s has not yet been upgraded to support prefix que
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/4b0ee66610c19815.
Report an issue: GitHub.