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.

Solutions

  1. Upgrade all nodes so every member understands each other's NodeState values
  2. Check which node/state triggered it in logs; if it's a leftover dead node, remove it from the cluster before the TCM migration
  3. Ensure the pre-upgrade Cassandra version is a supported migration baseline
  4. 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

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


AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10). Data as JSON: /api/errors/4b0ee66610c19815. Report an issue: GitHub.

Appendix: 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)