websockets/ws · error · Error
Parameter " " must have only a single value
Error message
Parameter "${key}" must have only a single value What it means
Thrown by `PerMessageDeflate.normalizeParams()` when a parameter appears more than once in a single extension configuration, i.e. its value array (produced by `extension.parse()` which collects repeats) has length > 1. RFC 7692 requires each parameter to occur at most once per offer, so duplicates are rejected.
Solutions
- Send each parameter at most once per extension offer.
- Generate the header with `extension.format()`, which never emits duplicates.
- Catch the error during the handshake and close with code 1002 (protocol error).
Example fix
// before — duplicated parameter
extension.parse('permessage-deflate; client_max_window_bits=10; client_max_window_bits=12');
pmd.normalizeParams(/* its output */);
// after — single value
extension.parse('permessage-deflate; client_max_window_bits=12'); Defensive patterns
Strategy: validation
Validate before calling
// Reject duplicate parameters before calling normalizeParams/accept.
function hasDuplicateParams(offer) {
return Object.values(offer).some((v) => Array.isArray(v) && v.length > 1);
}
if (parsedOffers.some(hasDuplicateParams)) {
abortHandshake(socket, 1002);
} Try / catch
try {
perMessageDeflate.accept(parsedOffers);
} catch (err) {
if (/must have only a single value/.test(err.message)) {
abortHandshake(socket, 1002);
return;
}
throw err;
} Prevention
- Send each permessage-deflate parameter at most once per offer.
- Use `extension.format()` to emit offers/responses (it never duplicates).
- Validate parsed offers for duplicate keys before negotiation.
When it happens
Trigger: A peer sends `Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits=10; client_max_window_bits=12`; after parsing this becomes `{ client_max_window_bits: ['10','12'] }` and `normalizeParams` throws. Triggered internally during `accept()`.
Common situations: A buggy client or proxy duplicates a parameter; a hand-crafted header with repeated keys; fuzzer input that stresses the handshake.
Related errors
- Invalid value for parameter
- None of the extension offers can be accepted
- Unexpected or invalid parameter "client_max_window_bits"
- Unknown parameter
- Unexpected parameter "client_no_context_takeover"
AI-assisted analysis of websockets/ws@c791e707ea (2026-08-06).
Data as JSON: /api/errors/d114715b4c2bde20.
Report an issue: GitHub.
Appendix: source
Thrown at lib/permessage-deflate.js:252
}
return params;
}
/**
* Normalize parameters.
*
* @param {Array} configurations The extension negotiation offers/reponse
* @return {Array} The offers/response with normalized parameters
* @private
*/
normalizeParams(configurations) {
configurations.forEach((params) => {
Object.keys(params).forEach((key) => {
let value = params[key];
if (value.length > 1) {
throw new Error(`Parameter "${key}" must have only a single value`);
}
value = value[0];
if (key === 'client_max_window_bits') {
if (value !== true) {
const num = +value;
if (!Number.isInteger(num) || num < 8 || num > 15) {
throw new TypeError(
`Invalid value for parameter "${key}": ${value}`
);
}
value = num;
} else if (!this._isServer) {
throw new TypeError(
`Invalid value for parameter "${key}": ${value}`
);
}View on GitHub (pinned to c791e707ea)