websockets/ws · error · TypeError

Second argument must be a string or a Uint8Array

Error message

Second argument must be a string or a Uint8Array

What it means

Thrown by Sender.close() when a 'data' argument is provided (it has a non-zero length) but is neither a string nor a Uint8Array (which includes Buffer). The close-frame body writer at lib/sender.js:204-210 only knows how to serialize strings (via buf.write) and Uint8Array views (via buf.set); any other type is rejected. This is a type-safety guard, not a protocol limit.

Source

Thrown at lib/sender.js:209

    } else if (data === undefined || !data.length) {
      buf = Buffer.allocUnsafe(2);
      buf.writeUInt16BE(code, 0);
    } else {
      const length = Buffer.byteLength(data);

      if (length > 123) {
        throw new RangeError('The message must not be greater than 123 bytes');
      }

      buf = Buffer.allocUnsafe(2 + length);
      buf.writeUInt16BE(code, 0);

      if (typeof data === 'string') {
        buf.write(data, 2);
      } else if (isUint8Array(data)) {
        buf.set(data, 2);
      } else {
        throw new TypeError('Second argument must be a string or a Uint8Array');
      }
    }

    const options = {
      [kByteLength]: buf.length,
      fin: true,
      generateMask: this._generateMask,
      mask,
      maskBuffer: this._maskBuffer,
      opcode: 0x08,
      readOnly: false,
      rsv1: false
    };

    if (this._state !== DEFAULT) {
      this.enqueue([this.dispatch, buf, false, options, cb]);
    } else {
      this.sendFrame(Sender.frame(buf, options), cb);

View on GitHub (pinned to ae1de54330)

Solutions

  1. Pass a string: ws.close(code, JSON.stringify(obj)) or ws.close(code, String(data)).
  2. Pass a Buffer or Uint8Array: ws.close(code, Buffer.from(payload)).
  3. Omit the data argument entirely if you only need the status code.

Example fix

// before
ws.close(1000, { reason: 'shutting down' });

// after
ws.close(1000, JSON.stringify({ reason: 'shutting down' }));
Defensive patterns

Strategy: type-guard

Validate before calling

function toCloseData(data) {
  if (data === undefined) return undefined;
  if (typeof data === 'string') return data;
  if (data instanceof Uint8Array) return data;
  return JSON.stringify(data);
}
// usage: ws.close(code, toCloseData(reason));

Type guard

function isClosePayload(data) {
  return data === undefined || typeof data === 'string' || data instanceof Uint8Array;
}

Try / catch

try {
  ws.close(code, data);
} catch (err) {
  if (err instanceof TypeError && /must be a string or a Uint8Array/.test(err.message)) {
    ws.close(code, String(data));
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: Calling ws.close(code, data) where data is a plain object, number, array, or any non-Buffer/non-Uint8Array/non-string value, e.g. ws.close(1000, { msg: 'bye' }). Also reachable via WebSocket.close(code, data) which forwards verbatim to Sender.close().

Common situations: Passing a structured object or Error instance as the close reason instead of a string; passing a number without converting to string; passing a Node.js object that is not a Buffer (e.g. a plain object that merely behaves like one).

Related errors


AI-assisted analysis of websockets/ws@ae1de54330 (2026-08-03). Data as JSON: /data/errors/e127a1f6a1af731a.json. Report an issue: GitHub.