redis/node-redis · error · MultiErrorReply

${errorIndexes.length} commands failed, see .replies and .er

Error message

${errorIndexes.length} commands failed, see .replies and .errorIndexes for more information

What it means

A MultiErrorReply thrown by RedisMultiCommand.transformReplies after a MULTI/EXEC (or pipeline) batch where at least one individual reply is an ErrorReply. It bundles every reply and the indexes that failed so callers can inspect which commands errored without losing the successful ones. The thrown object exposes .replies, .errorIndexes, and an .errors() generator.

Source

Thrown at packages/client/lib/multi-command.ts:74

    redisArgs.push(...args);

    this.addCommand(redisArgs, transformReply);
  }
  
  transformReplies(rawReplies: Array<unknown>): Array<unknown> {
    const errorIndexes: Array<number> = [],
      replies = rawReplies.map((reply, i) => {
        if (reply instanceof ErrorReply) {
          errorIndexes.push(i);
          return reply;
        }

        const { transformReply, args } = this.queue[i];
        return transformReply ? transformReply(reply, args.preserve, this.typeMapping) : reply;
      });

    if (errorIndexes.length) throw new MultiErrorReply(replies, errorIndexes);
    return replies;
  }
}

View on GitHub (pinned to bb5beb5657)

Solutions

  1. Catch the error and iterate err.errorIndexes / err.errors() to see which commands failed and why.
  2. Fix the individual command that produced the ErrorReply (read err.replies[i].message for the underlying cause).
  3. If partial success is acceptable, read err.replies for the non-error slots and proceed.
  4. Separate risky commands into their own transaction to isolate failures.

Example fix

// before
const replies = await multi.get('k1').set('k2','v2').exec();

// after
try {
  const replies = await multi.get('k1').set('k2','v2').exec();
} catch (e) {
  if (e.errorIndexes) {
    for (const [i, err] of e.errors()) console.error(`cmd #${i}:`, err.message);
    const ok = e.replies.filter((_, idx) => !e.errorIndexes.includes(idx));
  } else throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Cannot validate server-side command outcomes ahead of exec(). Validate inputs
// (types, arity) per queued command to reduce failures.

Type guard

function isMultiErrorReply(e) { return e instanceof Error && Array.isArray(e.errorIndexes) && Array.isArray(e.replies); }

Try / catch

try { return await multi.exec(); } catch (e) { if (isMultiErrorReply(e)) { for (const [i, err] of e.errors()) log(`#${i}`, err.message); return e.replies; } throw e; }

Prevention

When it happens

Trigger: Executing multi.exec(true) (typed/generic mode) where one queued command produced a server-side ErrorReply — e.g. a type error, WRONGTYPE, arity error, or a WATCH conditional abort (EXECABORT surfaces differently). Any pipeline/multi whose raw replies include Error instances.

Common situations: Batching heterogeneous commands where one key has the wrong type; a LUA script error in one slot of the pipeline; optimistic concurrency with WATCH where individual commands reject; one malformed command in a bulk batch.

Related errors


AI-assisted analysis of redis/node-redis@bb5beb5657 (2026-08-03). Data as JSON: /data/errors/cd9e30c81091ebe2.json. Report an issue: GitHub.