gchq/CyberChef · error · OperationError
Not handshake data.
Error message
Not handshake data.
What it means
Thrown by parseTLSRecord() in TLS.mjs when the first byte of the record (the TLS content type) is not 0x16. Value 0x16 designates a Handshake record; this parser only decodes handshake records. Any other content type (0x14 ChangeCipherSpec, 0x15 Alert, 0x17 ApplicationData, 0x16 itself absent) or non-TLS data is rejected up front before further parsing.
Source
Thrown at src/core/lib/TLS.mjs:30
/**
* Parse a TLS Record
* @param {Uint8Array} bytes
* @returns {JSON}
*/
export function parseTLSRecord(bytes) {
const s = new Stream(bytes);
const b = s.clone();
const r = {};
// Content type
r.contentType = {
description: "Content Type",
length: 1,
data: b.getBytes(1),
value: s.readInt(1)
};
if (r.contentType.value !== 0x16)
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.");View on GitHub (pinned to 4290ea7539)
Solutions
- Confirm the input is the bytes of a single TLS handshake record whose first byte equals 0x16.
- If parsing a stream, locate each record by reading the 5-byte TLS record header (type, version, length) and only pass handshake (0x16) records to this function.
- If you actually need to parse Alert/ApplicationData, this parser does not support those — use a full TLS dissector.
Example fix
// before parseTLSRecord(appDataRecordBytes); // first byte 0x17 // after: extract the handshake record first const rec = findRecordByType(stream, 0x16); parseTLSRecord(rec);
Defensive patterns
Strategy: validation
Validate before calling
function isHandshakeRecord(bytes) {
return bytes instanceof Uint8Array && bytes.length >= 1 && bytes[0] === 0x16;
}
if (!isHandshakeRecord(bytes)) {
throw new Error(
`Input is not a TLS handshake record (content type byte = 0x${(bytes[0] ?? 0).toString(16)}). ` +
`parseTLSRecord only decodes handshake (0x16) records.`
);
}
const r = parseTLSRecord(bytes); Type guard
function isHandshakeRecord(bytes) {
return (bytes instanceof Uint8Array || Array.isArray(bytes)) &&
bytes.length >= 1 && bytes[0] === 0x16;
} Try / catch
try {
record = parseTLSRecord(bytes);
} catch (e) {
if (e instanceof OperationError && /Not handshake data/.test(e.message)) {
// skip non-handshake records when walking a stream
continue;
}
throw e;
} Prevention
- When walking a TLS byte stream, read the 5-byte record header and dispatch on content type; only feed 0x16 records to parseTLSRecord.
- Do not pass arbitrary binary or application-data captures to this parser.
- For DTLS/QUIC or TLS 1.3 encrypted frames, use a dissector that supports them.
When it happens
Trigger: Calling parseTLSRecord() with bytes that are not a TLS handshake record: an ApplicationData record (0x17), an Alert (0x15), a full capture where the handshake is buried mid-stream, raw application traffic, or unrelated binary data. Also when the byte order is reversed or a TLS 1.3 encrypted ClientHello is fed in after encryption.
Common situations: Parsing a packet capture from the wrong record offset; feeding the entire TCP stream rather than the first record; trying to parse a ServerHello fragment that arrived alone after the ClientHello; using the parser on DTLS or QUIC framing which differ.
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 a known handshake message.
- Incorrect handshake length.
- Not handshake data.
- Incorrect handshake length.
AI-assisted analysis of gchq/CyberChef@4290ea7539 (2026-08-13).
Data as JSON: /api/errors/bd930c5a139fedf6.
Report an issue: GitHub.