{"record":{"id":"725105ee502a15df","repo":"can1357/oh-my-pi","slug":"truncated-compress-z-header","errorCode":null,"errorMessage":"Truncated compress (.Z) header","messagePattern":"Truncated compress \\(\\.Z\\) header","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/codecs/lzw.ts","lineNumber":101,"sourceCode":"\t\tif (needed <= this.#bytes.byteLength) {\n\t\t\treturn;\n\t\t}\n\t\tlet capacity = Math.max(needed, Math.min(this.#limit, Math.max(64, this.#bytes.byteLength * 2)));\n\t\tif (capacity > this.#limit) {\n\t\t\tcapacity = this.#limit;\n\t\t}\n\t\tconst grown = new Uint8Array(capacity);\n\t\tgrown.set(this.#bytes.subarray(0, this.#length));\n\t\tthis.#bytes = grown;\n\t}\n}\n\nfunction decode(bytes: Uint8Array, maxOutput: number): Uint8Array {\n\tif (!Number.isSafeInteger(maxOutput) || maxOutput < 0) {\n\t\tthrow new ArchiveError(\"Invalid compress (.Z) output limit\");\n\t}\n\tif (bytes.byteLength < 3) {\n\t\tthrow new ArchiveError(\"Truncated compress (.Z) header\");\n\t}\n\tif (bytes[0] !== 0x1f || bytes[1] !== 0x9d) {\n\t\tthrow new ArchiveError(\"Invalid compress (.Z) header\");\n\t}\n\n\tconst flags = bytes[2]!;\n\tif ((flags & 0x60) !== 0) {\n\t\tthrow new ArchiveError(\"Unsupported compress (.Z) header flags\");\n\t}\n\tconst maxBits = flags & 0x1f;\n\tif (maxBits < MIN_BITS || maxBits > MAX_BITS) {\n\t\tthrow new ArchiveError(`Invalid compress (.Z) maximum code width ${maxBits}`);\n\t}\n\tconst blockMode = (flags & 0x80) !== 0;\n\tconst dictionaryLimit = 2 ** maxBits;\n\tconst parents = new Uint16Array(dictionaryLimit);\n\tconst suffixes = new Uint8Array(dictionaryLimit);\n\tconst stack = new Uint8Array(dictionaryLimit);","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/codecs/lzw.ts#L83-L119","documentation":"A valid compress (.Z) stream begins with the 2-byte magic 0x1F 0x9D followed by a flags byte — at least 3 bytes total. This error is thrown when the input buffer is shorter than 3 bytes, so no header can even be read. (A 3+ byte input with wrong magic throws the sibling 'Invalid compress (.Z) header' instead.)","triggerScenarios":"Calling lzwDecompress with an empty, 1-byte, or 2-byte buffer — e.g. an empty file, a mis-sliced subarray, or reading zero bytes from a stream.","commonSituations":"Empty-file upload accepted without size checks; wrong path/column sliced from a container; a read that returned 0 bytes due to an IO error treated as success; passing the wrong variable to the decompressor.","solutions":["Check bytes.byteLength >= 3 (ideally the 0x1F9D magic) before calling lzwDecompress.","Verify the source file is non-empty and is actually a .Z file (`file data.Z`).","Fix the slicing/read logic that produced the short buffer.","Confirm you're not passing a wrong variable (e.g. a small header buffer instead of the full payload)."],"exampleFix":"// before\nconst data = await Bun.file(path).bytes();\nlzwDecompress(data, max);\n// after\nconst data = await Bun.file(path).bytes();\nif (data.byteLength < 3 || data[0] !== 0x1f || data[1] !== 0x9d) throw new Error(`Not a compress (.Z) file: ${path}`);\nlzwDecompress(data, max);","handlingStrategy":"type-guard","validationCode":"function looksLikeCompressZ(bytes: Uint8Array): boolean {\n  return bytes.byteLength >= 3 && bytes[0] === 0x1f && bytes[1] === 0x9d;\n}","typeGuard":"function isCompressZ(v: Uint8Array): v is Uint8Array & { length: 3 } {\n  return v.byteLength >= 3 && v[0] === 0x1f && v[1] === 0x9d;\n}","tryCatchPattern":"try {\n  return lzwDecompress(bytes, maxOutput);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.includes(\"header\")) {\n    throw new Error(`Input is not a compress (.Z) stream (${bytes.byteLength} bytes)`, { cause: err });\n  }\n  throw err;\n}","preventionTips":["Check the 0x1F 0x9D magic before decompressing; route non-.Z inputs to the right decoder by magic byte.","Reject empty/undersized files at ingestion time.","Confirm the variable passed to lzwDecompress is the full payload, not a header fragment.","Handle zero-byte reads from streams as errors, not valid inputs."],"tags":["archive","lzw","compress-z","truncated-input","argument-validation"],"backgroundTag":"truncated-compressed-stream","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}