{"record":{"id":"7e6b8694ca260557","repo":"different-ai/openwork","slug":"invalid-zip-local-header-for-entry-name","errorCode":null,"errorMessage":"Invalid ZIP local header for ${entry.name}.","messagePattern":"Invalid ZIP local header for (.+?)\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/desktop/electron/workspace-archive.mjs","lineNumber":221,"sourceCode":"    if (uncompressedSize > MAX_ENTRY_UNCOMPRESSED_BYTES) throw new Error(`Archive entry exceeds the ${MAX_ENTRY_UNCOMPRESSED_BYTES}-byte limit.`);\n    totalUncompressed += uncompressedSize;\n    if (totalUncompressed > MAX_TOTAL_UNCOMPRESSED_BYTES) throw new Error(\"Workspace archive contains too much uncompressed data.\");\n    const nameLength = buffer.readUInt16LE(cursor + 28);\n    const extraLength = buffer.readUInt16LE(cursor + 30);\n    const commentLength = buffer.readUInt16LE(cursor + 32);\n    const localOffset = buffer.readUInt32LE(cursor + 42);\n    const name = buffer.toString(\"utf8\", cursor + 46, cursor + 46 + nameLength);\n    entries.push({ name, method, compressedSize, uncompressedSize, localOffset });\n    cursor += 46 + nameLength + extraLength + commentLength;\n  }\n  return entries;\n}\n\nfunction readZipEntryData(buffer, entry) {\n  if (entry.uncompressedSize > MAX_ENTRY_UNCOMPRESSED_BYTES) throw new Error(`Archive entry exceeds the ${MAX_ENTRY_UNCOMPRESSED_BYTES}-byte limit.`);\n  const cursor = entry.localOffset;\n  if (buffer.readUInt32LE(cursor) !== ZIP_LOCAL_FILE_HEADER) {\n    throw new Error(`Invalid ZIP local header for ${entry.name}.`);\n  }\n  const nameLength = buffer.readUInt16LE(cursor + 26);\n  const extraLength = buffer.readUInt16LE(cursor + 28);\n  const dataStart = cursor + 30 + nameLength + extraLength;\n  const compressed = buffer.subarray(dataStart, dataStart + entry.compressedSize);\n  if (entry.method === 0) return compressed;\n  if (entry.method === 8) {\n    return zlib.inflateRawSync(compressed, {\n      finishFlush: zlib.constants.Z_SYNC_FLUSH,\n      maxOutputLength: MAX_ENTRY_UNCOMPRESSED_BYTES,\n    });\n  }\n  throw new Error(`Unsupported ZIP compression method ${entry.method} for ${entry.name}.`);\n}\n\nfunction isSafeArchivePath(name) {\n  if (!name || name.startsWith(\"/\") || /^[A-Za-z]:/.test(name)) return false;\n  const normalized = normalizeZipPath(name);","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/apps/desktop/electron/workspace-archive.mjs#L203-L239","documentation":"readZipEntryData validates the local file header signature (0x04034b50) at the entry's localOffset before reading entry data. If the bytes there are not the local header magic, the archive is corrupt, the offsets are wrong, or the file is not actually a ZIP, so extraction aborts with the entry name in the message.","triggerScenarios":"importWorkspaceConfig on a truncated, concatenated, or non-ZIP file that still passed a partial central-directory scan; an archive whose central directory offsets point at garbage; a renamed non-zip file given as archivePath.","commonSituations":"Partial upload/download of the archive file, a .zip produced by a tool writing data descriptors (streaming zip) with offset mismatches, or a user passing a text/JSON file renamed to .zip.","solutions":["Re-download/re-transfer or re-export the archive; the file is likely truncated or corrupt","Verify the file with `unzip -t archive.zip` or `file archive.zip` before importing","Ensure the source really is a ZIP produced by exportWorkspaceConfig, not a renamed file","Do not manually patch offsets; regenerate the archive from a working copy"],"exampleFix":"// before\nimportWorkspaceConfig({ archivePath: './notes.json.zip' })  // actually JSON\n// after\nfile notes.json.zip  # confirm 'Zip archive data'\nimportWorkspaceConfig({ archivePath: './workspace-export.zip' })","handlingStrategy":"validation","validationCode":"import { readFile } from 'node:fs/promises';\nasync function looksLikeZip(filePath) {\n  const buf = await readFile(filePath);\n  return buf.length > 4 && buf.readUInt32LE(0) === 0x04034b50;\n}","typeGuard":"function hasZipMagic(buf) {\n  return buf.length >= 4 && buf.readUInt32LE(0) === 0x04034b50;\n}","tryCatchPattern":"try {\n  await importWorkspaceConfig({ archivePath, targetDir });\n} catch (err) {\n  if (String(err.message).startsWith('Invalid ZIP local header')) {\n    // corrupt/truncated/non-zip file: re-export or re-download\n  } else throw err;\n}","preventionTips":["Run `unzip -t archive.zip` before importing","Avoid renaming non-zip files to .zip","Re-export the archive if a transfer was interrupted"],"tags":["zip","corrupt-archive","file-format"],"backgroundTag":"corrupt-zip-archive","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}