{"record":{"id":"7b36729f65594ff2","repo":"can1357/oh-my-pi","slug":"invalid-zip-archive-malformed-extra-field","errorCode":null,"errorMessage":"Invalid ZIP archive: malformed extra field","messagePattern":"Invalid ZIP archive: malformed extra field","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/zip.ts","lineNumber":264,"sourceCode":"\tconst day = date & 0x1f;\n\tconst hour = (time >>> 11) & 0x1f;\n\tconst minute = (time >>> 5) & 0x3f;\n\tconst second = (time & 0x1f) * 2;\n\tif (month < 0 || month > 11 || day < 1 || day > 31 || hour > 23 || minute > 59 || second > 59) return undefined;\n\tconst value = new Date(year, month, day, hour, minute, second).getTime();\n\treturn Number.isFinite(value) ? value : undefined;\n}\n\nfunction parseExtra(extra: Uint8Array, rawName: Uint8Array): ParsedExtra {\n\tconst result: ParsedExtra = {};\n\tlet offset = 0;\n\twhile (offset < extra.byteLength) {\n\t\tif (offset + 4 > extra.byteLength) throw new ArchiveError(\"Invalid ZIP archive: truncated extra-field header\");\n\t\tconst id = readUInt16LE(extra, offset);\n\t\tconst size = readUInt16LE(extra, offset + 2);\n\t\tconst dataStart = offset + 4;\n\t\tconst dataEnd = dataStart + size;\n\t\tif (dataEnd > extra.byteLength) throw new ArchiveError(\"Invalid ZIP archive: malformed extra field\");\n\t\tconst data = extra.subarray(dataStart, dataEnd);\n\t\tif (id === 0x0001) {\n\t\t\tresult.zip64 = data;\n\t\t} else if (id === 0x7075) {\n\t\t\tif (data.byteLength < 5) throw new ArchiveError(\"Invalid ZIP archive: Unicode path extra field is too small\");\n\t\t\tif (data[0] === 1 && readUInt32LE(data, 1) === crc32(rawName)) {\n\t\t\t\ttry {\n\t\t\t\t\tresult.unicodePath = UTF8_FATAL_DECODER.decode(data.subarray(5));\n\t\t\t\t} catch {\n\t\t\t\t\t// A bad optional Unicode path falls back to the header name.\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (id === 0x5455) {\n\t\t\tif (data.byteLength < 1)\n\t\t\t\tthrow new ArchiveError(\"Invalid ZIP archive: extended timestamp extra field is too small\");\n\t\t\tif ((data[0]! & 1) !== 0) {\n\t\t\t\tif (data.byteLength < 5)\n\t\t\t\t\tthrow new ArchiveError(\"Invalid ZIP archive: extended timestamp extra field is too small\");","sourceCodeStart":246,"sourceCodeEnd":282,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/zip.ts#L246-L282","documentation":"Thrown by parseExtra when an extra-field record's declared data size (id:u16, size:u16, then size bytes) extends beyond the end of the extra-field blob. The record header parses but its payload overruns — the enclosing extra_field_length is larger than the actual contained records.","triggerScenarios":"Calling zip info/read on an archive whose entry extra field contains a record whose size field exceeds the remaining bytes, e.g. corrupted size bytes or a writer that emitted oversized record headers.","commonSituations":"Bit corruption in the extra-field region; hand-edited archives; writers that compute record sizes in the wrong units (bytes vs words); fuzzed or malicious zips.","solutions":["Run `unzip -t archive.zip` to confirm corruption and identify the offending entry.","Re-zip the files with a standard tool to get consistent extra fields.","Repair with `zip -FF broken.zip --out fixed.zip`.","Verify transfer integrity (checksum) so corruption is caught before parsing."],"exampleFix":"// before: parsing a checksum-unverified transfer\nawait download(url, path);\nconst zip = readZip(path);\n// after: verify then parse\nawait download(url, path);\nif (await sha256File(path) !== manifest.sha256) throw new Error(\"corrupted download\");\nconst zip = readZip(path);","handlingStrategy":"validation","validationCode":"const probe = Bun.spawnSync([\"unzip\", \"-t\", path]);\nif (probe.exitCode !== 0) throw new Error(`archive structurally invalid per unzip -t; refusing to read: ${path}`);","typeGuard":null,"tryCatchPattern":"try {\n  const zip = readZip(path);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.includes(\"malformed extra field\")) {\n    // treat as corrupt input: repair or reject\n    await $`zip -FF ${path} --out fixed.zip`.nothrow();\n    return readZip(\"fixed.zip\");\n  }\n  throw err;\n}","preventionTips":["Gate untrusted uploads through `unzip -t` before ingestion.","Verify transfer checksums so corruption is caught before parsing.","Standardize on writers that compute record sizes correctly (Info-ZIP/7-Zip).","Keep quarantined copies of archives that fail structural probes for diagnosis."],"tags":["zip","archive","extra-field","corruption"],"backgroundTag":"zip-extra-field-malformed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}