redis/node-redis · error · Error
Cannot split ${label}: malformed numkeys argument '${args[ke
Error message
Cannot split ${label}: malformed numkeys argument '${args[keyNumIdx]}' What it means
For `keynum` specs, the numkeys argument read from the command args must be a positive integer. A non-integer, zero, negative, or non-numeric value (e.g. 'abc', '2.5', '0', '') cannot be used to compute the key region, so the splitter refuses with the offending value in the message. Unlike most splitter errors, this is a USER-ARGUMENT error: the caller passed a bad numkeys.
Source
Thrown at packages/client/lib/cluster/request-response-policies/multi-shard-splitter.ts:92
// ranges (lastKey >= 0) and limit can be added when a command needs them.
if (findKeys.lastKey !== -1 || findKeys.limit !== 0) {
throw new Error(`Cannot split ${label}: unsupported find_keys range (lastkey ${findKeys.lastKey}, limit ${findKeys.limit})`);
}
keyStep = findKeys.keyStep;
keyRegionStart = start;
keyRegionEnd = args.length;
break;
}
case 'keynum': {
keyStep = findKeys.keyStep;
keyNumIdx = start + findKeys.keyNumIdx;
keyRegionStart = start + findKeys.firstKey;
if (keyNumIdx >= keyRegionStart) {
throw new Error(`Cannot split ${label}: numkeys argument inside the key region`);
}
const numKeys = parsePositiveInteger(args[keyNumIdx]);
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}`);
}View on GitHub (pinned to bb5beb5657)
Solutions
- Pass a valid positive integer for the numkeys argument matching the number of key groups you provide.
- Use the typed command API rather than raw sendCommand so argument validation happens before routing.
- If using a keynum command, ensure numkeys exactly equals the trailing key/value groups.
Example fix
// before
await cluster.sendCommand(['MSEDEX', 'abc', '{a}1', 'v1', 'NX', 'EX', '10']);
// throws: Cannot split MSEDEX: malformed numkeys argument 'abc'
// after — numkeys is a positive integer matching the key groups
await cluster.sendCommand(['MSEDEX', '1', '{a}1', 'v1', 'NX', 'EX', '10']); Defensive patterns
Strategy: validation
Validate before calling
// Validate numkeys before issuing a keynum-spec multi_shard command.
function assertValidNumkeys(numkeys: unknown): asserts numkeys is number {
const n = Number(numkeys);
if (!Number.isInteger(n) || n <= 0) {
throw new TypeError(`numkeys must be a positive integer, got ${JSON.stringify(numkeys)}`);
}
}
assertValidNumkeys(process.argv[2]);
await cluster.sendCommand(['MSEDEX', String(numkeys), ...keyGroups, 'NX', 'EX', '10']); Type guard
function isPositiveInteger(value: unknown): value is number {
return Number.isInteger(Number(value)) && Number(value) > 0;
} Try / catch
try {
await cluster.sendCommand(['MSEDEX', numkeys, ...keyGroups, 'NX', 'EX', '10']);
} catch (e) {
if (/malformed numkeys/.test(e.message)) {
// caller passed a bad numkeys — fix the argument
} else throw e;
} Prevention
- Pass a positive integer for numkeys matching the number of key groups supplied.
- Prefer the typed command API over raw sendCommand so arg validation happens before routing.
- Compute numkeys from the actual key array length rather than hardcoding it.
When it happens
Trigger: Issuing a keynum-spec multi_shard command (e.g. a hypothetical MSETEX) with a numkeys argument that is not a positive integer. Note: MSEDEX is currently curated OUT of multi_shard (routes default-keyed like MSETNX), so the keynum branch has no live caller in the shipped metadata — this fires only if a keynum-spec command is re-enabled or added.
Common situations: A raw sendCommand with a wrong numkeys; a future keynum multi_shard command where the caller mis-counts keys; a buffer/whitespace numkeys argument.
Related errors
- Cannot split ${label}: key region overruns the arguments
- Cannot split ${label}: command has no key specification
- Cannot split ${label}: multiple key specifications are not s
- Cannot split ${label}: unsupported begin_search type '${begi
- Cannot split ${label}: unsupported find_keys range (lastkey
AI-assisted analysis of redis/node-redis@bb5beb5657 (2026-08-03).
Data as JSON: /data/errors/ec8c47685176c140.json.
Report an issue: GitHub.