{"record":{"id":"ed5184b879118565","repo":"can1357/oh-my-pi","slug":"invalid-zip-archive-malformed-ntfs-extra-field","errorCode":null,"errorMessage":"Invalid ZIP archive: malformed NTFS extra field","messagePattern":"Invalid ZIP archive: malformed NTFS extra field","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/zip.ts","lineNumber":235,"sourceCode":"\treturn info;\n}\n\nfunction filetimeToMs(bytes: Uint8Array, offset: number): number | undefined {\n\tlet value = 0n;\n\tfor (let index = 7; index >= 0; index--) value = (value << 8n) | BigInt(bytes[offset + index]!);\n\tconst milliseconds = value / 10_000n - WINDOWS_EPOCH_FILETIME_MS;\n\tconst number = Number(milliseconds);\n\treturn Number.isSafeInteger(number) ? number : undefined;\n}\n\nfunction parseNtfsMtime(data: Uint8Array): number | undefined {\n\tif (data.byteLength < 4) return undefined;\n\tlet offset = 4;\n\twhile (offset + 4 <= data.byteLength) {\n\t\tconst tag = readUInt16LE(data, offset);\n\t\tconst size = readUInt16LE(data, offset + 2);\n\t\tconst end = offset + 4 + size;\n\t\tif (end > data.byteLength) throw new ArchiveError(\"Invalid ZIP archive: malformed NTFS extra field\");\n\t\tif (tag === 1 && size >= 8) return filetimeToMs(data, offset + 4);\n\t\toffset = end;\n\t}\n\treturn undefined;\n}\n\nfunction parseDosMtime(time: number, date: number): number | undefined {\n\tif (date === 0) return undefined;\n\tconst year = 1980 + ((date >>> 9) & 0x7f);\n\tconst month = ((date >>> 5) & 0x0f) - 1;\n\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}","sourceCodeStart":217,"sourceCodeEnd":253,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/zip.ts#L217-L253","documentation":"Thrown by parseNtfsMtime while walking the attributes inside a ZIP NTFS extra field (id 0x000a): a sub-attribute record's tag+size runs past the end of the field data. The library treats a malformed NTFS timestamp field as a hard error rather than silently ignoring the optional timestamp.","triggerScenarios":"Calling zip info/read on an archive whose entries carry an NTFS extra field (0x000a) with a reserved 4-byte prefix plus attribute records where any record's declared size overflows the field. Triggered only when this optional extra field is present and internally inconsistent.","commonSituations":"Archives written by tools that emit NTFS timestamps with incorrect attribute lengths; archives corrupted in the extra-field region; hand-crafted or fuzzed zips.","solutions":["Test with `unzip -t`; if standard tools accept it, report the incompatibility and re-zip the files.","Recreate the archive with a standard tool (Info-ZIP, 7-Zip), ideally without NTFS timestamp extras if the reader chokes on them.","Repair with `zip -FF` if the source archive is corrupt.","Strip the offending extra field with a zip-manipulation script before reading."],"exampleFix":"// before: reading the problematic archive as-is\nconst zip = readZip(badArchive);\n// after: re-zip cleanly to drop nonstandard extras\nawait $`unzip -q badArchive.zip -d tmp`;\nawait $`zip -qr clean.zip .`.cwd(\"tmp\");\nconst zip = readZip(\"clean.zip\");","handlingStrategy":"try-catch","validationCode":"// Not cheaply pre-validable without parsing extras; at minimum confirm the archive passes a reference reader.\nconst probe = Bun.spawnSync([\"unzip\", \"-t\", path]);\nif (probe.exitCode !== 0) throw new Error(`archive fails `unzip -t`; repair or re-zip before reading: ${path}`);","typeGuard":null,"tryCatchPattern":"try {\n  const zip = readZip(path);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.includes(\"malformed NTFS extra field\")) {\n    // writer emits broken NTFS timestamps: re-zip without extras\n    await $`unzip -q ${path} -d tmp`; await $`zip -qr clean.zip .`.cwd(\"tmp\");\n    return readZip(\"clean.zip\");\n  }\n  throw err;\n}","preventionTips":["Run `unzip -t` as an ingestion gate for third-party archives.","Prefer archives from standard writers; be suspicious of custom backup tools that emit NTFS extras.","When re-zipping, drop nonessential extended attributes if consumers are strict parsers.","Report malformed archives to their producer rather than special-casing the parser."],"tags":["zip","archive","extra-field","ntfs","corruption"],"backgroundTag":"zip-extra-field-malformed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}