{"record":{"id":"d1f0691251e0b221","repo":"apache/cassandra","slug":"replacing-a-node-without-bootstrapping-risks-inval","errorCode":null,"errorMessage":"Replacing a node without bootstrapping risks invalidating consistency guarantees as the expected data may not be present until repair is run. To perform this operation, please restart with -Dcassandra.allow_unsafe_replace=true","messagePattern":"Replacing a node without bootstrapping risks invalidating consistency guarantees as the expected data may not be present until repair is run\\. To perform this operation, please restart with -Dcassandra\\.allow_unsafe_replace=true","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/tcm/sequences/BootstrapAndReplace.java","lineNumber":445,"sourceCode":"               Objects.equals(latestModification, that.latestModification) &&\n               Objects.equals(lockKey, that.lockKey) &&\n               Objects.equals(bootstrapTokens, that.bootstrapTokens) &&\n               Objects.equals(startReplace, that.startReplace) &&\n               Objects.equals(midReplace, that.midReplace) &&\n               Objects.equals(finishReplace, that.finishReplace);\n    }\n\n    @Override\n    public int hashCode()\n    {\n        return Objects.hash(latestModification, lockKey, bootstrapTokens, startReplace, midReplace, finishReplace, next, finishJoiningRing, streamData);\n    }\n\n    public static void checkUnsafeReplace(boolean shouldBootstrap)\n    {\n        if (!shouldBootstrap && !CassandraRelevantProperties.ALLOW_UNSAFE_REPLACE.getBoolean())\n        {\n            throw new RuntimeException(\"Replacing a node without bootstrapping risks invalidating consistency \" +\n                                       \"guarantees as the expected data may not be present until repair is run. \" +\n                                       \"To perform this operation, please restart with \" +\n                                       \"-Dcassandra.allow_unsafe_replace=true\");\n        }\n\n    }\n\n    public static void gossipStateToHibernate(ClusterMetadata metadata, NodeId nodeId)\n    {\n        if (nodeId == NodeId.UNREGISTERED)\n            return;\n        // order is important here, the gossiper can fire in between adding these two states.  It's ok to send TOKENS without STATUS, but *not* vice versa.\n        List<Pair<ApplicationState, VersionedValue>> states = new ArrayList<>();\n        VersionedValue.VersionedValueFactory valueFactory = StorageService.instance.valueFactory;\n        states.add(Pair.create(ApplicationState.TOKENS, valueFactory.tokens(metadata.tokenMap.tokens(nodeId))));\n        states.add(Pair.create(ApplicationState.STATUS_WITH_PORT, valueFactory.hibernate(true)));\n        states.add(Pair.create(ApplicationState.STATUS, valueFactory.hibernate(true)));\n        Gossiper.instance.addLocalApplicationStates(states);","sourceCodeStart":427,"sourceCodeEnd":463,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/tcm/sequences/BootstrapAndReplace.java#L427-L463","documentation":"checkUnsafeReplace refuses to replace a node without bootstrapping when the system property cassandra.allow_unsafe_replace is not set to true. Replacing without streaming data in can leave the ring without the expected replicas until a repair runs, so Cassandra forces the operator to opt in explicitly at startup. It is a deliberate operator-safety gate, not a bug.","triggerScenarios":"Calling BootstrapAndReplace.checkUnsafeReplace(false) (or a replace path invoking it) on a node not started with -Dcassandra.allow_unsafe_replace=true.","commonSituations":"Hostile take-over replace procedures, replacing a dead node in a lab where streaming is intentionally skipped, automation scripts that skip bootstrap but forgot the JVM property, operator following an old runbook for 'replace_address' without bootstrapping.","solutions":["If bootstrapping is intended, ensure shouldBootstrap=true is passed / the node performs a normal replace with data streaming","If intentionally skipping bootstrap, restart the node with -Dcassandra.allow_unsafe_replace=true","After an unsafe replace, run a full repair (nodetool repair) to restore replica consistency","Never set the flag in production without a plan to repair immediately"],"exampleFix":"// before (cassandra-env.sh)\n# no flag set\n// after (cassandra-env.sh)\nJVM_OPTS=\"$JVM_OPTS -Dcassandra.allow_unsafe_replace=true\" // only for lab/CI clusters","handlingStrategy":"validation","validationCode":"boolean unsafeAllowed = Boolean.getBoolean(\"cassandra.allow_unsafe_replace\");\nif (!shouldBootstrap && !unsafeAllowed)\n    throw new IllegalStateException(\"Refusing replace-without-bootstrap: set -Dcassandra.allow_unsafe_replace=true to override\");","typeGuard":null,"tryCatchPattern":"try { checkUnsafeReplace(false); } catch (RuntimeException e) { promptForConfirmationAndRestartWithFlag(); }","preventionTips":["Always use normal (bootstrapping) replace in production","Document that allow_unsafe_replace requires an immediate full repair","Keep the flag out of production cassandra-env.sh"],"tags":["replace","bootstrap","operator-safety","config-flag"],"backgroundTag":"missing-required-flag","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}