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
- Pass a string: ws.close(code, JSON.stringify(obj)) or ws.close(code, String(data)).
- Pass a Buffer or Uint8Array: ws.close(code, Buffer.from(payload)).
- 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
- Never pass plain objects or numbers as the close reason; serialize first.
- Coerce unknown input through a helper that returns string|Buffer|undefined.
- If migrating from a library that accepted objects, wrap calls at the boundary.
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
- The message must not be greater than 123 bytes
- The data size must not be greater than 125 bytes
- Invalid URL: ${address}
- An invalid or duplicated subprotocol was specified
- ws does not work in the browser. Browser clients must use th
AI-assisted analysis of websockets/ws@ae1de54330 (2026-08-03).
Data as JSON: /data/errors/e127a1f6a1af731a.json.
Report an issue: GitHub.