apache/cassandra · error · IllegalStateException
Unable to start because the node is not in the normal state.
Error message
Unable to start %s because the node is not in the normal state.
What it means
checkServiceAllowedToStart(service) requires the node to be in NORMAL mode before starting services, but only when joinRing is true. If the node hasn't finished joining the ring (it stays in STARTING or another non-normal mode), starting the service is refused because a gossip-only/non-joined node shouldn't serve clients. Thrown as IllegalStateException for nodetool display.
Solutions
- Wait for the node to reach NORMAL (log line 'Node / state jump to normal' or `nodetool status`) before starting services
- If bootstrap failed, fix the cause (schema disagreement, streaming failure) and restart the node
- Set join_ring=false only if you intend a gossip-only coordinator; the check is skipped in that case
- Check system.log for bootstrap/streaming errors blocking the transition to NORMAL
Example fix
// before: start transport immediately at boot
storageService.startNativeTransport();
// after: start only once the node is in NORMAL mode
if (storageService.isNormal())
storageService.startNativeTransport(); Defensive patterns
Strategy: validation
Validate before calling
StorageServiceMBean ss = ...;
if (!ss.getOperationMode().equals("NORMAL")) {
throw new IllegalStateException("wait for node to join ring before starting services");
} Type guard
boolean canStartServices(StorageServiceMBean ss) {
return ss.getOperationMode().equals("NORMAL") || !ss.isJoined(); // join_ring=false nodes skip check
} Try / catch
try { startNativeTransport(); } catch (IllegalStateException e) { if (e.getMessage().contains("not in the normal state")) { retryWithBackoffAfterJoin(); } else { throw e; } } Prevention
- Wait for 'state jump to normal' log line before enabling services
- Monitor bootstrap completion rather than process liveness
- Fix any bootstrap failures (schema disagreement, streaming errors) before restarting services
When it happens
Trigger: Starting native/rpc transport while the node is still bootstrapping, joining, or otherwise not NORMAL (e.g. calling startRPCServer early at startup, or a node stuck bootstrapping with join_ring=true).
Common situations: Custom startup scripts starting transport before the node joins the ring; nodes stuck in STARTING due to failed bootstrap/schema disagreement; join_ring misconfiguration.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Booting with ClusterMetadata from file:
- Can't abort bootstrap for - it is alive
- Can't abort bootstrap for node since the state is
- Expected to complete startup sequence, but did not. Can't…
- Found no candidates during initialization. Check if the…
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/6342cc40d720add0.
Report an issue: GitHub.
Appendix: source
Thrown at src/java/org/apache/cassandra/service/StorageService.java:4163
return postShutdownHooks.remove(hook);
}
/**
* Some services are shutdown during draining and we should not attempt to start them again.
*
* @param service - the name of the service we are trying to start.
* @throws IllegalStateException - an exception that nodetool is able to convert into a message to display to the user
*/
synchronized void checkServiceAllowedToStart(String service)
{
if (isDraining()) // when draining isShutdown is also true, so we check first to return a more accurate message
throw new IllegalStateException(String.format("Unable to start %s because the node is draining.", service));
if (isShutdown()) // do not rely on operationMode in case it gets changed to decommissioned or other
throw new IllegalStateException(String.format("Unable to start %s because the node was drained.", service));
if (!isNormal() && joinRing) // if the node is not joining the ring, it is gossipping-only member which is in STARTING state forever
throw new IllegalStateException(String.format("Unable to start %s because the node is not in the normal state.", service));
}
// Never ever do this at home. Used by tests.
@VisibleForTesting
public IPartitioner setPartitionerUnsafe(IPartitioner newPartitioner)
{
checkNotNull(newPartitioner, "newPartitioner is null");
checkState(originalPartitioner == null, "Already changed the partitioner without resetting");
originalPartitioner = DatabaseDescriptor.setPartitionerUnsafe(newPartitioner);
valueFactory = new VersionedValue.VersionedValueFactory(newPartitioner);
return originalPartitioner;
}
@VisibleForTesting
public void resetPartitionerUnsafe()
{
checkState(originalPartitioner != null, "Original partitioner was never changed");
DatabaseDescriptor.setPartitionerUnsafe(originalPartitioner);View on GitHub (pinned to 88fd0f6a0e)