redis/node-redis · error · TypeError

"arguments[${i}]" must be of type "string | Buffer", got ${t

Error message

"arguments[${i}]" must be of type "string | Buffer", got ${typeof arg} instead.

What it means

Thrown by the RESP command encoder (encodeCommand) when iterating over command arguments: every arg must be either a string or a Buffer so it can be length-prefixed and written to the wire. If any element is a number, boolean, object, null, or undefined, the encoder cannot compute its byte length and rejects it with a TypeError naming the offending index and the actual typeof. This guards the lowest layer of the client against emitting malformed RESP.

Source

Thrown at packages/client/lib/RESP/encoder.ts:21

const CRLF = '\r\n';

export default function encodeCommand(args: ReadonlyArray<RedisArgument>): ReadonlyArray<RedisArgument> {
  const toWrite: Array<RedisArgument> = [];

  let strings = '*' + args.length + CRLF;

  for (let i = 0; i < args.length; i++) {
    const arg = args[i];
    if (typeof arg === 'string') {
      strings += '$' + Buffer.byteLength(arg) + CRLF + arg + CRLF;
    } else if (arg instanceof Buffer) {
      toWrite.push(
        strings + '$' + arg.length.toString() + CRLF,
        arg
      );
      strings = CRLF;
    } else {
      throw new TypeError(`"arguments[${i}]" must be of type "string | Buffer", got ${typeof arg} instead.`);
    }
  }

  toWrite.push(strings);

  return toWrite;
}

View on GitHub (pinned to bb5beb5657)

Solutions

  1. Coerce each dynamic argument to a string before passing it: `String(value)` or `Buffer.from(value)`.
  2. If the value is binary, pass it as a Buffer: `Buffer.from(value)`.
  3. For custom commands, ensure parseCommand only ever pushes string/Buffer via parser.push / parser.pushVariadic with pre-converted values.
  4. Validate/serialize at the boundary where untrusted JSON enters your Redis call layer.

Example fix

// before
await client.sendCommand(['SET', 'counter', 123]);

// after
await client.sendCommand(['SET', 'counter', String(123)]);
Defensive patterns

Strategy: validation

Validate before calling

function toRedisArg(value: unknown): string | Buffer {
  if (typeof value === 'string') return value;
  if (Buffer.isBuffer(value)) return value;
  throw new TypeError(`Cannot encode ${typeof value} as a Redis argument`);
}
// usage: client.sendCommand(['SET', 'k', toRedisArg(maybeNumber) as string]);

Type guard

function isRedisArgument(arg: unknown): arg is string | Buffer {
  return typeof arg === 'string' || Buffer.isBuffer(arg);
}

Try / catch

try {
  await client.sendCommand(args);
} catch (err) {
  if (err instanceof TypeError && /must be of type/.test(err.message)) {
    // an argument was not string|Buffer; serialize and retry, or surface a config error
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling `client.sendCommand([42])` or `client.set('k', 123)` where 123 is a number (some commands coerce, but raw sendCommand and custom commands do not). Passing an object/undefined/null as a command argument, or using `client.multi().addCommand(['GET', someObject])`. Building args from untyped JSON without stringifying numeric/boolean fields.

Common situations: Passing numeric counters/IDs straight from JS logic into a command; forgetting to `String()` or `.toString()` values read from JSON/env; mixing a loosely-typed data layer with the strongly-typed Redis client; writing a custom Command whose parseCommand pushes a non-coerced variable.

Related errors


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