microsoft/garnet · error · Exception

Invalid SETSLOT Operation

Error message

Invalid SETSLOT Operation

What it means

Thrown by MigrationDriver when mapping a MigrateState to the byte sequence sent via CLUSTER SETSLOTRANGE. Only IMPORT, STABLE, and NODE are valid SETSLOT states; SUCCESS, FAIL, PENDING (and invalid casts) are rejected. It protects the wire protocol from emitting an undefined SETSLOT opcode.

Source

Thrown at libs/cluster/Server/Migration/MigrationDriver.cs:34

        /// <param name="state"></param>
        /// <returns></returns>
        public async Task<bool> TrySetSlotRangesAsync(string nodeid, MigrateState state)
        {
            var client = migrateOperation[0].Client;
            try
            {
                if (!await CheckConnectionAsync(client).ConfigureAwait(false))
                {
                    Status = MigrateState.FAIL;
                    return false;
                }

                var stateBytes = state switch
                {
                    MigrateState.IMPORT => IMPORTING,
                    MigrateState.STABLE => STABLE,
                    MigrateState.NODE => NODE,
                    _ => throw new Exception("Invalid SETSLOT Operation"),
                };

                logger?.LogTrace("Sending CLUSTER SETSLOTRANGE {state} {nodeid} {slots}", state, nodeid ?? "null", ClusterManager.GetRange([.. _sslots]));

                var result = await client.SetSlotRange(stateBytes, nodeid, _slotRanges)
                    .WaitAsync(_timeout, _cts.Token).ConfigureAwait(false);

                // Check if setslotsrange executed correctly
                if (!result.Equals("OK", StringComparison.Ordinal))
                {
                    logger?.LogError("SetSlotRange error: {error}", result);
                    Status = MigrateState.FAIL;
                    return false;
                }

                logger?.LogTrace("[Completed] SETSLOT {slots} {state} {nodeid}", ClusterManager.GetRange([.. _sslots]), state, nodeid ?? "");
                return true;
            }

View on GitHub (pinned to 951b0fc683)

Solutions

  1. Pass only MigrateState.IMPORT, MigrateState.STABLE, or MigrateState.NODE to the slot-range setter.
  2. Separate progress/result states from slot-config states at the call site to prevent the mix-up.
  3. Add a Debug.Assert on the incoming state before the switch.

Example fix

// before
var stateBytes = state switch
{
    MigrateState.IMPORT => IMPORTING,
    MigrateState.STABLE => STABLE,
    MigrateState.NODE => NODE,
    _ => throw new Exception("Invalid SETSLOT Operation"),
};
// after
Debug.Assert(state is MigrateState.IMPORT or MigrateState.STABLE or MigrateState.NODE);
var stateBytes = state switch
{
    MigrateState.IMPORT => IMPORTING,
    MigrateState.STABLE => STABLE,
    MigrateState.NODE => NODE,
};
Defensive patterns

Strategy: validation

Validate before calling

// Validate slot-config state before SETSLOTRANGE
if (state is not (MigrateState.IMPORT or MigrateState.STABLE or MigrateState.NODE))
    throw new ArgumentOutOfRangeException(nameof(state), state, "State must be IMPORT, STABLE, or NODE");

Prevention

When it happens

Trigger: Calling the SETSLOT-range sender with state set to SUCCESS, FAIL, PENDING, or an out-of-range value instead of one of the three valid slot-configuration states.

Common situations: A migration driver caller passes a result/progress state (SUCCESS/FAIL/PENDING) where a slot-state (IMPORT/STABLE/NODE) is expected, due to mixing up the two roles of the MigrateState enum.

Related errors


AI-assisted analysis of microsoft/garnet@951b0fc683 (2026-08-13). Data as JSON: /api/errors/72e329bbe4c8826e. Report an issue: GitHub.