gchq/CyberChef · error · OperationError
Not handshake data.
Error message
Not handshake data.
What it means
JA3Fingerprint.run() reads the first byte of the TLS record and requires 0x16 (Handshake content type). Anything else (or undefined when the stream is empty, since Stream.readInt returns undefined past the end) throws. OperationError surfaced as step output.
Source
Thrown at src/core/operations/JA3Fingerprint.mjs:64
value: ["Hash digest", "JA3 string", "Full details"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [inputFormat, outputFormat] = args;
input = Utils.convertToByteArray(input, inputFormat);
const s = new Stream(new Uint8Array(input));
const handshake = s.readInt(1);
if (handshake !== 0x16)
throw new OperationError("Not handshake data.");
// Version
s.moveForwardsBy(2);
// Length
const length = s.readInt(2);
if (s.length !== length + 5)
throw new OperationError("Incorrect handshake length.");
// Handshake type
const handshakeType = s.readInt(1);
if (handshakeType !== 1)
throw new OperationError("Not a Client Hello.");
// Handshake length
const handshakeLength = s.readInt(3);
if (s.length !== handshakeLength + 9)
throw new OperationError("Not enough data in Client Hello.");View on GitHub (pinned to 4290ea7539)
Solutions
- Verify the first byte equals 0x16; if not, re-slice the input to the record boundary.
- Make the inputFormat selector match the actual data (Hex/Base64/Raw).
- Capture/extract only the ClientHello record, not the whole session.
- If the data is application data (0x17) or another content type, you need a different starting point.
Example fix
// before
ja3.run('GET / HTTP/1.1\r\n', ['Latin1','Base64']); // first byte 'G'=0x47 -> Not handshake data.
// after
const rec = hexStartingWith16; // begins 16 03 01 ...
ja3.run(rec, ['Hex','Base64']); Defensive patterns
Strategy: validation
Validate before calling
import Utils from "src/core/Utils.mjs";
function assertTlsHandshakeRecord(input, inputFormat) {
const bytes = Utils.convertToByteArray(input, inputFormat);
if (bytes.length < 1) throw new Error('empty input');
if (bytes[0] !== 0x16) {
throw new Error(`First byte 0x${bytes[0].toString(16)} is not a TLS Handshake (0x16). Check inputFormat and record boundary.`);
}
return bytes;
} Type guard
function looksLikeTlsHandshake(bytes) {
return bytes.length >= 5 && bytes[0] === 0x16 && bytes[1] === 0x03;
} Prevention
- Verify the first byte is 0x16 before running JA3/JA3S.
- Keep inputFormat in sync with the data (Hex/Base64/Raw).
- Feed a single record, not a full session.
- Capture exactly the ClientHello for JA3.
When it happens
Trigger: Input is not a TLS record (HTTP, random bytes), or inputFormat does not match the data so convertToByteArray produced wrong bytes. Also when feeding a record that does not start at a record boundary (mid-stream) or an application-data record (0x17).
Common situations: Captured a full TCP stream instead of just the ClientHello; selected 'Hex' but pasted raw text (or vice versa); fed a ServerHello (JA3 not JA3S); started reading after the content-type byte.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Incorrect handshake length.
- Not a Client Hello.
- Not enough data in Client Hello.
- Not handshake data.
- Incorrect handshake length.
AI-assisted analysis of gchq/CyberChef@4290ea7539 (2026-08-13).
Data as JSON: /api/errors/0bdc42eed32cdcc3.
Report an issue: GitHub.