{"record":{"id":"ed89787687ec170d","repo":"gchq/CyberChef","slug":"incorrect-handshake-length","errorCode":null,"errorMessage":"Incorrect handshake length.","messagePattern":"Incorrect handshake length\\.","errorType":"exception","errorClass":"OperationError","httpStatus":null,"severity":"error","filePath":"src/core/lib/TLS.mjs","lineNumber":48,"sourceCode":"        throw new OperationError(\"Not handshake data.\");\n\n    // Version\n    r.version = {\n        description: \"Protocol Version\",\n        length: 2,\n        data: b.getBytes(2),\n        value: s.readInt(2)\n    };\n\n    // Length\n    r.length = {\n        description: \"Record Length\",\n        length: 2,\n        data: b.getBytes(2),\n        value: s.readInt(2)\n    };\n    if (s.length !== r.length.value + 5)\n        throw new OperationError(\"Incorrect handshake length.\");\n\n    // Handshake\n    r.handshake = {\n        description: \"Handshake\",\n        length: r.length.value,\n        data: b.getBytes(r.length.value),\n        value: parseHandshake(s.getBytes(r.length.value))\n    };\n\n    return r;\n}\n\n/**\n * Parse a TLS Handshake\n * @param {Uint8Array} bytes\n * @returns {JSON}\n */\nfunction parseHandshake(bytes) {","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/gchq/CyberChef/blob/4290ea753912378913b1f3f54e0fc5720afeda5d/src/core/lib/TLS.mjs#L30-L66","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nparseTLSRecord(combinedBuffer); // contains record + trailing bytes\n// after\nconst len = (buf[3] << 8) | buf[4];\nparseTLSRecord(buf.slice(0, 5 + len));","handlingStrategy":"validation","validationCode":"function sliceOneRecord(buf) {\n    if (buf.length < 5) throw new Error(\"Buffer too short for a TLS record header\");\n    const len = (buf[3] << 8) | buf[4];\n    if (buf.length !== 5 + len) {\n        throw new Error(\n            `Record length mismatch: declared ${len} bytes but buffer has ${buf.length - 5} payload bytes.`\n        );\n    }\n    return buf.slice(0, 5 + len);\n}\nconst r = parseTLSRecord(sliceOneRecord(buf));","typeGuard":"function isExactlyOneRecord(buf) {\n    return buf.length >= 5 && buf.length === 5 + ((buf[3] << 8) | buf[4]);\n}","tryCatchPattern":"try {\n    record = parseTLSRecord(buf);\n} catch (e) {\n    if (e instanceof OperationError && /Incorrect handshake length/.test(e.message)) {\n        // buffer is truncated or contains multiple records; reassemble or slice\n        return await refillAndRetry();\n    }\n    throw e;\n}","preventionTips":["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."],"tags":["tls","parsing","record-framing","length-validation"],"backgroundTag":null,"analyzedSha":"4290ea753912378913b1f3f54e0fc5720afeda5d","analyzedAt":"2026-08-13T06:05:50.210Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}