websockets/ws · error · TypeError
First argument must be a valid error code number
Error message
First argument must be a valid error code number
What it means
Thrown (as TypeError) by `Sender.close()` when the `code` argument is provided but is not a number, or is a number that fails `isValidStatusCode()`. Per RFC 6455 §7.4, the only valid status codes are 1000–1014 (excluding the reserved 1004, 1005, and 1006) and the application range 3000–4999. Codes 1005/1006 must never be sent on the wire (they are generated internally), and 1004 is undefined/unused, so the library rejects them.
Solutions
- Use a defined code: 1000 (normal closure), 1001 (going away), 1002 (protocol error), 1003 (unsupported data), 1007–1011, or a custom 3000–4999.
- Coerce string inputs to numbers and check the range before calling `close()`.
- Omit `code` (and `reason`) entirely to send a close frame with no status code — `ws.close()` is always valid.
Example fix
// before ws.close(999, 'done'); // after ws.close(1000, 'done');
Defensive patterns
Strategy: validation
Validate before calling
// Validate a close code before calling ws.close().
function isValidStatusCode(code) {
return (
(code >= 1000 &&
code <= 1014 &&
code !== 1004 &&
code !== 1005 &&
code !== 1006) ||
(code >= 3000 && code <= 4999)
);
}
function safeClose(ws, code, reason) {
if (code === undefined) return ws.close();
if (typeof code !== 'number' || !isValidStatusCode(code)) return ws.close();
return ws.close(code, reason);
} Type guard
function isValidCloseCode(code) {
return (
typeof code === 'number' &&
Number.isInteger(code) &&
((code >= 1000 &&
code <= 1014 &&
code !== 1004 &&
code !== 1005 &&
code !== 1006) ||
(code >= 3000 && code <= 4999))
);
} Try / catch
try {
ws.close(maybeCode, reason);
} catch (err) {
if (err instanceof TypeError && /valid error code/.test(err.message)) {
// Fall back to a code-less close frame.
ws.close();
return;
}
throw err;
} Prevention
- Always pass an integer in 1000–1014 (not 1004/1005/1006) or 3000–4999, or omit the code entirely.
- Coerce status codes read from config/env to numbers and validate the range before calling `close()`.
- Wrap programmatic `close()` calls in a helper that validates the code, so a bad value degrades to a code-less close.
When it happens
Trigger: Calling `ws.close(999, 'reason')` (below range), `ws.close(1005)` (reserved, must not be sent), `ws.close(2000, 'reason')` (outside 3000–4999), `ws.close('1000')` (string not number), or `ws.close(undefined, 'reason')` passed through carelessly.
Common situations: Passing a string status code from a config/env var without coercion; using a 'magic' code outside the defined ranges; copy-pasting a code that is reserved (1004/1005/1006); passing a close code from the wrong end of a mapping table.
Related errors
- Unexpected character at index
- Unexpected end of input
- The data size must not be greater than 125 bytes
- The message must not be greater than 123 bytes
- The " " subprotocol is duplicated
AI-assisted analysis of websockets/ws@c791e707ea (2026-08-06).
Data as JSON: /api/errors/21fd53d0970da278.
Report an issue: GitHub.
Appendix: source
Thrown at lib/sender.js:190
return [target, data];
}
/**
* Sends a close message to the other peer.
*
* @param {Number} [code] The status code component of the body
* @param {(String|Buffer)} [data] The message component of the body
* @param {Boolean} [mask=false] Specifies whether or not to mask the message
* @param {Function} [cb] Callback
* @public
*/
close(code, data, mask, cb) {
let buf;
if (code === undefined) {
buf = EMPTY_BUFFER;
} else if (typeof code !== 'number' || !isValidStatusCode(code)) {
throw new TypeError('First argument must be a valid error code number');
} 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 {View on GitHub (pinned to c791e707ea)