{"id":"e127a1f6a1af731a","repo":"websockets/ws","slug":"second-argument-must-be-a-string-or-a-uint8array","errorCode":null,"errorMessage":"Second argument must be a string or a Uint8Array","messagePattern":"Second argument must be a string or a Uint8Array","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"lib/sender.js","lineNumber":209,"sourceCode":"    } else if (data === undefined || !data.length) {\n      buf = Buffer.allocUnsafe(2);\n      buf.writeUInt16BE(code, 0);\n    } else {\n      const length = Buffer.byteLength(data);\n\n      if (length > 123) {\n        throw new RangeError('The message must not be greater than 123 bytes');\n      }\n\n      buf = Buffer.allocUnsafe(2 + length);\n      buf.writeUInt16BE(code, 0);\n\n      if (typeof data === 'string') {\n        buf.write(data, 2);\n      } else if (isUint8Array(data)) {\n        buf.set(data, 2);\n      } else {\n        throw new TypeError('Second argument must be a string or a Uint8Array');\n      }\n    }\n\n    const options = {\n      [kByteLength]: buf.length,\n      fin: true,\n      generateMask: this._generateMask,\n      mask,\n      maskBuffer: this._maskBuffer,\n      opcode: 0x08,\n      readOnly: false,\n      rsv1: false\n    };\n\n    if (this._state !== DEFAULT) {\n      this.enqueue([this.dispatch, buf, false, options, cb]);\n    } else {\n      this.sendFrame(Sender.frame(buf, options), cb);","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/websockets/ws/blob/ae1de54330cef77e487548890fabfeb9aae1d83d/lib/sender.js#L191-L227","documentation":"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.","triggerScenarios":"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().","commonSituations":"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).","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."],"exampleFix":"// before\nws.close(1000, { reason: 'shutting down' });\n\n// after\nws.close(1000, JSON.stringify({ reason: 'shutting down' }));","handlingStrategy":"type-guard","validationCode":"function toCloseData(data) {\n  if (data === undefined) return undefined;\n  if (typeof data === 'string') return data;\n  if (data instanceof Uint8Array) return data;\n  return JSON.stringify(data);\n}\n// usage: ws.close(code, toCloseData(reason));","typeGuard":"function isClosePayload(data) {\n  return data === undefined || typeof data === 'string' || data instanceof Uint8Array;\n}","tryCatchPattern":"try {\n  ws.close(code, data);\n} catch (err) {\n  if (err instanceof TypeError && /must be a string or a Uint8Array/.test(err.message)) {\n    ws.close(code, String(data));\n  } else {\n    throw err;\n  }\n}","preventionTips":["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."],"tags":["websocket","type-error","input-validation","control-frame"],"analyzedSha":"ae1de54330cef77e487548890fabfeb9aae1d83d","analyzedAt":"2026-08-03T19:11:18.437Z","schemaVersion":2}