{"record":{"id":"530f7dba52153e43","repo":"gchq/CyberChef","slug":"malformed-lznt1-stream-invalid-shift","errorCode":null,"errorMessage":"Malformed LZNT1 stream: Invalid shift!","messagePattern":"Malformed LZNT1 stream: Invalid shift!","errorType":"exception","errorClass":"OperationError","httpStatus":null,"severity":"error","filePath":"src/core/lib/LZNT1.mjs","lineNumber":73,"sourceCode":"            while (coffset < blockEnd) {\n                let header = compressed[coffset++];\n\n                for (let i = 0; i < 8 && coffset < blockEnd; i++) {\n                    if ((header & 1) === 0) {\n                        decompressed.push(compressed[coffset++]);\n                    } else {\n                        const pointer = Utils.byteArrayToInt(compressed.slice(coffset, coffset + 2), \"little\");\n                        coffset += 2;\n\n                        const displacement = getDisplacement(decompressed.length - doffset - 1);\n                        const symbolOffset = (pointer >> (12 - displacement)) + 1;\n                        const symbolLength = (pointer & (0xFFF >> displacement)) + 2;\n                        const shiftOffset = decompressed.length - symbolOffset;\n\n                        for (let shiftDelta = 0; shiftDelta < symbolLength + 1; shiftDelta++) {\n                            const shift = shiftOffset + shiftDelta;\n                            if (shift < 0 || decompressed.length <= shift) {\n                                throw new OperationError(\"Malformed LZNT1 stream: Invalid shift!\");\n                            }\n                            decompressed.push(decompressed[shift]);\n                        }\n                    }\n                    header >>= 1;\n                }\n            }\n        } else {\n            decompressed.push(...compressed.slice(coffset, coffset + size + 1));\n            coffset += size + 1;\n        }\n    }\n\n    return decompressed;\n}\n","sourceCodeStart":55,"sourceCodeEnd":89,"githubUrl":"https://github.com/gchq/CyberChef/blob/4290ea753912378913b1f3f54e0fc5720afeda5d/src/core/lib/LZNT1.mjs#L55-L89","documentation":"Inside an LZNT1 compressed block, flag bits introduce 2-byte back-reference pointers. The decoder computes a source offset into the already-decompressed buffer and copies bytes from there. If the computed shift is below 0 or beyond the current decompressed length, the back-reference points outside valid data and the stream is malformed.","triggerScenarios":"Calling LZNT1.decompress on a stream whose compressed back-reference pointer resolves to an offset not yet written (negative shift or past end). Indicates the pointer/displacement bits do not correspond to a legitimate LZ77-style reference.","commonSituations":"Corrupted compressed attribute (bit-flip in storage); data tampering; feeding a different LZ variant (e.g. XPRESS/LZXPRESS) into the LZNT1 decoder; partial overwrite of the compressed buffer; wrong endianness when slicing the input.","solutions":["Re-acquire the compressed bytes from the authoritative source to rule out corruption.","Confirm the algorithm is actually LZNT1 (used by NTFS) and not a sibling LZ format.","Compare the byte sequence against a known-good reference decoder output to localize the bad pointer.","If you only need best-effort recovery, copy bytes up to the failing offset and accept partial output (wrap in try/catch and keep the partial `decompressed` array)."],"exampleFix":"// before\nconst out = LZNT1.decompress(bytes); // throws mid-stream, loses all output\n\n// after - capture partial output for forensic/recovery use\nlet partial;\ntry {\n  partial = LZNT1.decompress(bytes);\n} catch (e) {\n  if (/Invalid shift/.test(e.message)) {\n    // re-run a tolerant decoder that stops at the bad block\n    partial = tolerantLZNT1(bytes);\n  } else throw e;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"let partial;\ntry {\n  partial = LZNT1.decompress(bytes);\n} catch (e) {\n  if (!(e instanceof OperationError) || !/Invalid shift/.test(e.message)) throw e;\n  partial = tolerantLZNT1Until(bytes, failOffset); // custom partial decoder\n}\nreturn partial;","preventionTips":["Treat an 'Invalid shift' as data corruption, not a code bug.","Re-acquire the source bytes from the authoritative copy.","Confirm the stream is genuinely LZNT1 (NTFS) and not a related LZ variant.","Validate a checksum of the compressed attribute before decompressing."],"tags":["compression","lznt1","ntfs","data-corruption"],"backgroundTag":null,"analyzedSha":"4290ea753912378913b1f3f54e0fc5720afeda5d","analyzedAt":"2026-08-13T06:05:50.210Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}