different-ai/openwork · error

Invalid ZIP local header for ${entry.name}.

Error message

Invalid ZIP local header for ${entry.name}.

What it means

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.

Source

Thrown at apps/desktop/electron/workspace-archive.mjs:221

    if (uncompressedSize > MAX_ENTRY_UNCOMPRESSED_BYTES) throw new Error(`Archive entry exceeds the ${MAX_ENTRY_UNCOMPRESSED_BYTES}-byte limit.`);
    totalUncompressed += uncompressedSize;
    if (totalUncompressed > MAX_TOTAL_UNCOMPRESSED_BYTES) throw new Error("Workspace archive contains too much uncompressed data.");
    const nameLength = buffer.readUInt16LE(cursor + 28);
    const extraLength = buffer.readUInt16LE(cursor + 30);
    const commentLength = buffer.readUInt16LE(cursor + 32);
    const localOffset = buffer.readUInt32LE(cursor + 42);
    const name = buffer.toString("utf8", cursor + 46, cursor + 46 + nameLength);
    entries.push({ name, method, compressedSize, uncompressedSize, localOffset });
    cursor += 46 + nameLength + extraLength + commentLength;
  }
  return entries;
}

function readZipEntryData(buffer, entry) {
  if (entry.uncompressedSize > MAX_ENTRY_UNCOMPRESSED_BYTES) throw new Error(`Archive entry exceeds the ${MAX_ENTRY_UNCOMPRESSED_BYTES}-byte limit.`);
  const cursor = entry.localOffset;
  if (buffer.readUInt32LE(cursor) !== ZIP_LOCAL_FILE_HEADER) {
    throw new Error(`Invalid ZIP local header for ${entry.name}.`);
  }
  const nameLength = buffer.readUInt16LE(cursor + 26);
  const extraLength = buffer.readUInt16LE(cursor + 28);
  const dataStart = cursor + 30 + nameLength + extraLength;
  const compressed = buffer.subarray(dataStart, dataStart + entry.compressedSize);
  if (entry.method === 0) return compressed;
  if (entry.method === 8) {
    return zlib.inflateRawSync(compressed, {
      finishFlush: zlib.constants.Z_SYNC_FLUSH,
      maxOutputLength: MAX_ENTRY_UNCOMPRESSED_BYTES,
    });
  }
  throw new Error(`Unsupported ZIP compression method ${entry.method} for ${entry.name}.`);
}

function isSafeArchivePath(name) {
  if (!name || name.startsWith("/") || /^[A-Za-z]:/.test(name)) return false;
  const normalized = normalizeZipPath(name);

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Re-download/re-transfer or re-export the archive; the file is likely truncated or corrupt
  2. Verify the file with `unzip -t archive.zip` or `file archive.zip` before importing
  3. Ensure the source really is a ZIP produced by exportWorkspaceConfig, not a renamed file
  4. Do not manually patch offsets; regenerate the archive from a working copy

Example fix

// before
importWorkspaceConfig({ archivePath: './notes.json.zip' })  // actually JSON
// after
file notes.json.zip  # confirm 'Zip archive data'
importWorkspaceConfig({ archivePath: './workspace-export.zip' })
Defensive patterns

Strategy: validation

Validate before calling

import { readFile } from 'node:fs/promises';
async function looksLikeZip(filePath) {
  const buf = await readFile(filePath);
  return buf.length > 4 && buf.readUInt32LE(0) === 0x04034b50;
}

Type guard

function hasZipMagic(buf) {
  return buf.length >= 4 && buf.readUInt32LE(0) === 0x04034b50;
}

Try / catch

try {
  await importWorkspaceConfig({ archivePath, targetDir });
} catch (err) {
  if (String(err.message).startsWith('Invalid ZIP local header')) {
    // corrupt/truncated/non-zip file: re-export or re-download
  } else throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/7e6b8694ca260557. Report an issue: GitHub.