redis/node-redis · error · Error

Cannot split : key region does not align with keystep

Error message

Cannot split ${label}: key region does not align with keystep ${keyStep}

What it means

The key region length is not a positive multiple of keyStep, meaning the arguments contain an incomplete key group. For MSET (keyStep 2) a flat array with an odd number of elements leaves the last key without a value, so the splitter cannot form valid sub-commands.

Solutions

  1. Ensure flat arrays have even length for key-value pair commands (MSET keyStep is 2)
  2. Use tuple arrays [[key, value], ...] or Record objects for type safety instead of flat arrays
  3. Validate before sending: arr.length % keyStep === 0

Example fix

// before
await client.mSet(['key1', 'val1', 'key2']);
// after
await client.mSet(['key1', 'val1', 'key2', 'val2']);
Defensive patterns

Strategy: validation

Validate before calling

// For flat key-value arrays: ensure even length (keyStep is 2 for MSET).
function isBalancedKvArray(arr: unknown[]): boolean {
  return arr.length > 0 && arr.length % 2 === 0;
}

if (!isBalancedKvArray(pairs)) {
  throw new Error('Flat MSET array must have an even number of elements');
}

Type guard

function isEvenLength(arr: Array<unknown>): arr is Array<unknown> & { length: number } {
  return arr.length % 2 === 0;
}

Prevention

When it happens

Trigger: Calling client.mSet(['key1','value1','key2']) in cluster mode — 3 elements with keyStep 2 leaves 'key2' without a value. Also triggered by any flat-array multi-key command with an unbalanced argument count.

Common situations: Dynamically building a flat key-value array and omitting a value; an off-by-one in a loop that constructs pairs; data source returning an odd number of fields.

Related errors


AI-assisted analysis of redis/node-redis@90fd0652bc (2026-08-11). Data as JSON: /api/errors/3295b573beb013fb. Report an issue: GitHub.

Appendix: source

Thrown at packages/client/lib/cluster/request-response-policies/multi-shard-splitter.ts:109

      if (numKeys === undefined) {
        throw new Error(`Cannot split ${label}: malformed numkeys argument '${args[keyNumIdx]}'`);
      }
      keyRegionEnd = keyRegionStart + numKeys * keyStep;
      break;
    }
    default:
      throw new Error(`Cannot split ${label}: unsupported find_keys type '${findKeys.type}'`);
  }

  if (keyStep < 1) {
    throw new Error(`Cannot split ${label}: invalid keystep ${keyStep}`);
  }
  if (keyRegionStart < 1 || keyRegionEnd > args.length) {
    throw new Error(`Cannot split ${label}: key region overruns the arguments`);
  }
  const regionLength = keyRegionEnd - keyRegionStart;
  if (regionLength <= 0 || regionLength % keyStep !== 0) {
    throw new Error(`Cannot split ${label}: key region does not align with keystep ${keyStep}`);
  }

  const groupCount = regionLength / keyStep;
  const slotGroups = new Map<number, Array<number>>();
  for (let group = 0; group < groupCount; group++) {
    const slot = calculateSlot(args[keyRegionStart + group * keyStep]);
    const groups = slotGroups.get(slot);
    if (groups) {
      groups.push(group);
    } else {
      slotGroups.set(slot, [group]);
    }
  }

  const subCommands = new Map<number, SubCommand>();

  // Single-slot fast path: nothing to split — pass the original command
  // through untouched (also preserves single-slot atomicity). Keys keep their

View on GitHub (pinned to 90fd0652bc)