{"record":{"id":"19d998d08b37b6f5","repo":"apache/cassandra","slug":"can-t-revert-move-from","errorCode":null,"errorMessage":"Can't revert move from ","messagePattern":"Can't revert move from ","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/tcm/sequences/Move.java","lineNumber":445,"sourceCode":"        ClusterMetadata metadata = ClusterMetadata.current();\n        return new ProgressBarrier(latestModification, metadata.directory.location(startMove.nodeId()), metadata.lockedRanges.locked.get(lockKey));\n    }\n\n    @Override\n    public ClusterMetadata.Transformer cancel(ClusterMetadata metadata)\n    {\n        DataPlacements placements = metadata.placements();\n        switch (next)\n        {\n            case FINISH_MOVE:\n                placements = midMove.inverseDelta().apply(metadata.directory, metadata.nextEpoch(), placements);\n            case MID_MOVE:\n                placements = startMove.inverseDelta().apply(metadata.directory, metadata.nextEpoch(), placements);\n            case START_MOVE:\n                placements = toSplitRanges.invert().apply(metadata.directory, metadata.nextEpoch(), placements);\n                break;\n            default:\n                throw new IllegalStateException(\"Can't revert move from \" + next);\n        }\n\n        LockedRanges newLockedRanges = metadata.lockedRanges.unlock(lockKey);\n        return metadata.transformer()\n                       .withNodeState(startMove.nodeId(), NodeState.JOINED)\n                       .with(placements)\n                       .with(newLockedRanges);\n    }\n\n    /**\n     * Returns a mapping of destination -> source*, where the destination is the node that needs to stream from source\n     *\n     * there can be multiple sources for each destination\n     */\n    private static MovementMap movementMap(IFailureDetector fd, DataPlacements placements, EndpointLookup endpointLookup, PlacementDeltas toSplitRanges, PlacementDeltas toStart, PlacementDeltas midDeltas, boolean strictConsistency)\n    {\n        MovementMap.Builder allMovements = MovementMap.builder();\n        toStart.forEach((params, delta) -> {","sourceCodeStart":427,"sourceCodeEnd":463,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/tcm/sequences/Move.java#L427-L463","documentation":"Move.cancel reverts placed ranges step by step from MID_MOVE or START_MOVE back to the pre-move state. If the current step (next) is neither MID_MOVE nor START_MOVE, there is no defined way to revert, so cancel throws IllegalStateException naming the step. This prevents corrupting metadata by cancelling a move that is not in a revertible phase.","triggerScenarios":"Calling cancel on a Move sequence whose current MultiStepOperation step is FINISH_MOVE, COMPLETED, or otherwise not in the START_MOVE/MID_MOVE phases handled by the switch.","commonSituations":"Operator tries to cancel a move after it already finished; race between the move completing and the cancel command arriving; automation retrying cancel after the move already committed its final step.","solutions":["Check the move's current step before cancelling; a completed/finished move cannot be reverted via cancel","If the move is at FINISH_MOVE, let it complete or use the appropriate completion/rollback path","Retry cancel promptly after the move is observed in START_MOVE/MID_MOVE if reverting is truly required","Verify TCM metadata state of the sequence to confirm which step it is actually on"],"exampleFix":"// before\nmove.cancel();\n// after\nif (move.currentStep() == Move.MoveStep.START_MOVE || move.currentStep() == Move.MoveStep.MID_MOVE)\n    move.cancel();\nelse\n    logger.warn(\"Move is at {} and cannot be reverted\", move.currentStep());","handlingStrategy":"validation","validationCode":"Move.MoveStep step = move.currentStep();\nif (step != Move.MoveStep.START_MOVE && step != Move.MoveStep.MID_MOVE)\n    throw new IllegalStateException(\"Move not revertible at \" + step);","typeGuard":null,"tryCatchPattern":"try { move.cancel(); } catch (IllegalStateException e) { logger.warn(\"Cannot cancel move: {}\", e.getMessage()); inspectSequenceState(); }","preventionTips":["Check the sequence's current step before issuing cancel","Don't cancel after FINISH_MOVE; let it complete","Automate cancellation promptly after detecting a stuck START/MID move"],"tags":["illegal-state","move","cancel","rollback"],"backgroundTag":"invalid-state-transition","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}