{"record":{"id":"6f611a5c63b42efa","repo":"can1357/oh-my-pi","slug":"invalid-zip-archive-truncated-data-for-memberp","errorCode":null,"errorMessage":"Invalid ZIP archive: truncated data for '${memberPath}'","messagePattern":"Invalid ZIP archive: truncated data for '(.+?)'","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/zip.ts","lineNumber":426,"sourceCode":"\t\t\tconst localFlags = readUInt16LE(header, 6);\n\t\t\tif ((localFlags & (ENCRYPTED_FLAG | STRONG_ENCRYPTION_FLAG)) !== 0) {\n\t\t\t\tthrow new ArchiveError(`Encrypted ZIP member '${memberPath}' is not supported`);\n\t\t\t}\n\t\t\tif (readUInt16LE(header, 8) !== this.#method) {\n\t\t\t\tthrow new ArchiveError(\n\t\t\t\t\t`Invalid ZIP archive: local and central compression methods disagree for '${memberPath}'`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst dataStart = this.#localHeaderOffset + 30 + readUInt16LE(header, 26) + readUInt16LE(header, 28);\n\t\t\tconst dataEnd = checkedEnd(dataStart, this.#compressedSize, this.#source.size, `data for '${memberPath}'`);\n\t\t\tif (this.#method === 0 && this.#compressedSize !== size) {\n\t\t\t\tthrow new ArchiveError(\n\t\t\t\t\t`Invalid ZIP archive: size mismatch for '${memberPath}' (expected ${size}, got ${this.#compressedSize})`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst compressed = await this.#source.read(dataStart, dataEnd);\n\t\t\tif (compressed.byteLength !== this.#compressedSize) {\n\t\t\t\tthrow new ArchiveError(`Invalid ZIP archive: truncated data for '${memberPath}'`);\n\t\t\t}\n\t\t\tconst decoded = await decodeMember(compressed, this.#method, size, memberPath);\n\t\t\tif (decoded.byteLength !== size) {\n\t\t\t\tthrow new ArchiveError(\n\t\t\t\t\t`Invalid ZIP archive: size mismatch for '${memberPath}' (expected ${size}, got ${decoded.byteLength})`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst actualCrc = crc32(decoded);\n\t\t\tif (actualCrc !== this.#crc) {\n\t\t\t\tthrow new ArchiveError(`Invalid ZIP archive: CRC mismatch for '${memberPath}'`);\n\t\t\t}\n\t\t\treturn decoded;\n\t\t} catch (error) {\n\t\t\tthrow archiveError(error, `Failed to read ZIP member '${memberPath}'`);\n\t\t}\n\t}\n}\n","sourceCodeStart":408,"sourceCodeEnd":444,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/zip.ts#L408-L444","documentation":"Thrown when the bytes actually read for a member are fewer than the declared compressed size — the data region extends past the end of the source. This means the archive is truncated: the central directory claims more payload bytes than exist. ArchiveError naming the member.","triggerScenarios":"ZipMemberSource read where source.read(dataStart, dataEnd) returns short because localHeaderOffset + 30 + nameLen + extraLen + compressedSize exceeds source size — typical of cut-off downloads, partial uploads, or a source buffer smaller than the archive.","commonSituations":"HTTP downloads interrupted before completion; files copied while still being written; fetching a zip over a flaky network and not verifying Content-Length; trimmed logs/attachments containing embedded zips.","solutions":["Re-download the archive and compare byte length against the server's Content-Length or a published checksum","Check disk space / copy completion — the file on disk may be short","For HTTP sources, ensure the full body was read before parsing (await the stream end)","Use `unzip -t` to confirm which members are recoverable; extract those individually"],"exampleFix":"// before: parsing before body completes\nconst res = await fetch(url);\nconst zip = await readZip(Bun.file(await res.arrayBuffer())); // may throw 3715 if body short\n// after: verify length\nconst buf = new Uint8Array(await res.arrayBuffer());\nif (buf.byteLength !== Number(res.headers.get('content-length'))) throw new Error('truncated download');\nconst zip = await readZip(buf);","handlingStrategy":"validation","validationCode":"const bytes = new Uint8Array(await file.arrayBuffer());\nif (bytes.byteLength < declaredZipSize) throw new Error('archive truncated — re-download before parsing');","typeGuard":null,"tryCatchPattern":"try {\n  const data = await zip.read(member);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.includes('truncated data')) {\n    throw new Error('download incomplete; refetch with length verification');\n  }\n  throw err;\n}","preventionTips":["Always compare downloaded size to Content-Length or a checksum before parsing","Fully consume HTTP bodies / streams before handing bytes to the reader","Guard against copying files that are still being written (watch for stable size)","Retry with resumable downloads for large archives"],"tags":["zip","truncated-data","corrupt-archive","download"],"backgroundTag":"truncated-archive","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}