gchq/CyberChef · error · OperationError
Incorrect handshake length.
Error message
Incorrect handshake length.
What it means
Thrown by parseTLSRecord() in TLS.mjs when the declared record length field (2 bytes at offset 3-4) plus the 5-byte header does not equal the total bytes supplied to the parser. The check `s.length !== r.length.value + 5` ensures the record is neither truncated nor padded with extra bytes. A mismatch means the byte buffer is not a complete, exactly-sized handshake record.
Source
Thrown at src/core/lib/TLS.mjs:48
throw new OperationError("Not handshake data.");
// Version
r.version = {
description: "Protocol Version",
length: 2,
data: b.getBytes(2),
value: s.readInt(2)
};
// Length
r.length = {
description: "Record Length",
length: 2,
data: b.getBytes(2),
value: s.readInt(2)
};
if (s.length !== r.length.value + 5)
throw new OperationError("Incorrect handshake length.");
// Handshake
r.handshake = {
description: "Handshake",
length: r.length.value,
data: b.getBytes(r.length.value),
value: parseHandshake(s.getBytes(r.length.value))
};
return r;
}
/**
* Parse a TLS Handshake
* @param {Uint8Array} bytes
* @returns {JSON}
*/
function parseHandshake(bytes) {View on GitHub (pinned to 4290ea7539)
Solutions
- Slice the input to exactly 5 + declared-length bytes: const rec = buf.slice(0, 5 + readUint16BE(buf, 3)).
- If the buffer is shorter than declared, continue reading from the source until the full record is available (TLS records are self-describing; reassemble by length).
- If the buffer is longer, iterate records using the length field and pass each one individually.
Example fix
// before parseTLSRecord(combinedBuffer); // contains record + trailing bytes // after const len = (buf[3] << 8) | buf[4]; parseTLSRecord(buf.slice(0, 5 + len));
Defensive patterns
Strategy: validation
Validate before calling
function sliceOneRecord(buf) {
if (buf.length < 5) throw new Error("Buffer too short for a TLS record header");
const len = (buf[3] << 8) | buf[4];
if (buf.length !== 5 + len) {
throw new Error(
`Record length mismatch: declared ${len} bytes but buffer has ${buf.length - 5} payload bytes.`
);
}
return buf.slice(0, 5 + len);
}
const r = parseTLSRecord(sliceOneRecord(buf)); Type guard
function isExactlyOneRecord(buf) {
return buf.length >= 5 && buf.length === 5 + ((buf[3] << 8) | buf[4]);
} Try / catch
try {
record = parseTLSRecord(buf);
} catch (e) {
if (e instanceof OperationError && /Incorrect handshake length/.test(e.message)) {
// buffer is truncated or contains multiple records; reassemble or slice
return await refillAndRetry();
}
throw e;
} Prevention
- Always slice buffers to exactly one record using the 5-byte header + declared length.
- Reassemble across TCP segments until you have the full declared length before parsing.
- Never feed a stream of concatenated records to a single parseTLSRecord call.
When it happens
Trigger: Passing a truncated handshake record (network MTU split, capture cut short), a buffer containing the record plus trailing bytes from the next record, or a reassembled buffer with the wrong byte range. Also when the length field itself is malformed or endianness is wrong.
Common situations: TCP segmentation delivering a partial record; merging multiple records into one buffer and feeding all of them; off-by-N slicing when extracting the record from a larger frame; reading the length field as signed or little-endian by mistake.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Not enough data in Handshake message.
- Not handshake data.
- Not a known handshake message.
- Not handshake data.
- Incorrect handshake length.
AI-assisted analysis of gchq/CyberChef@4290ea7539 (2026-08-13).
Data as JSON: /api/errors/ed89787687ec170d.
Report an issue: GitHub.