apache/cassandra · error · java.lang.IllegalStateException

Can't resume a move operation unless it has failed

Error message

Can't resume a move operation unless it has failed

What it means

resumeMove only resumes moves that FAILED: StorageService.operationMode() must be MOVE_FAILED. Otherwise it throws IllegalStateException 'Can't resume a move operation unless it has failed', preventing resuming an operation that is healthy or in another mode.

Solutions

  1. Let the in-progress move finish on its own; resume is only for failed moves.
  2. If the move is stuck but not marked failed, abort it (`nodetool abortmove`) and start a new move.
  3. Check `nodetool netstats` / logs to see whether streaming for the move is still active before resuming.
Defensive patterns

Strategy: validation

Validate before calling

if (StorageService.instance.operationMode() != StorageService.Mode.MOVE_FAILED)
    return; // only failed moves can be resumed

Type guard

boolean canResumeMove() { return StorageService.instance.operationMode() == StorageService.Mode.MOVE_FAILED; }

Try / catch

try { resumeMove(); }
catch (IllegalStateException e) { if (e.getMessage().contains("unless it has failed")) { waitOrAbort(); } else throw e; }

Prevention

When it happens

Trigger: Running `nodetool resumemove` while a MOVE sequence is in progress but the node's operation mode is not MOVE_FAILED (e.g. NORMAL, MOVE in progress, or LEAVING).

Common situations: Operators resuming a move that is still running (not failed); leftover transient mode cleared by a prior command; issuing resume after a manual mode change.

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


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

Appendix: source

Thrown at src/java/org/apache/cassandra/tcm/sequences/SingleNodeSequences.java:234

        ClusterMetadata metadata = ClusterMetadata.current();
        NodeId self = metadata.myNodeId();
        MultiStepOperation<?> sequence = metadata.inProgressSequences.get(self);
        if (sequence == null || sequence.kind() != MultiStepOperation.Kind.MOVE)
        {
            String msg = "No move operation in progress, can't resume";
            logger.info(msg);
            if (StorageService.instance.operationMode() == MOVE_FAILED)
            {
                // there is no ongoing move to resume, but operation mode thinks there is
                StorageService.instance.clearTransientMode();
            }
            throw new IllegalStateException(msg);
        }
        if (StorageService.instance.operationMode() != MOVE_FAILED)
        {
            String msg = "Can't resume a move operation unless it has failed";
            logger.info(msg);
            throw new IllegalStateException(msg);
        }
        StorageService.instance.clearTransientMode();
        InProgressSequences.finishInProgressSequences(self);
    }

    static void abortMove(String nodeId)
    {
        abortHelper(nodeId, MultiStepOperation.Kind.MOVE, MOVE_FAILED);
    }

    /**
     *
     * @param nodeId node id to abort the MSO for, null for local node
     * @param kind the expected kind of the multi step operation to abort
     * @param ssMode the legacy mode we want storage service to be in, null for any
     */
    private static void abortHelper(@Nullable String nodeId, MultiStepOperation.Kind kind, @Nullable StorageService.Mode ssMode)
    {

View on GitHub (pinned to 88fd0f6a0e)