nexu-io/open-design · error · Error

invalid zip central directory

Error message

invalid zip central directory

What it means

Thrown by `readCentralDirectory` when the end-of-central-directory record's `centralOffset + centralSize` exceeds the actual archive buffer length. The central directory points past the end of the file, so the archive is truncated or corrupt and cannot be safely parsed.

Source

Thrown at apps/daemon/src/design/claude-design-import.ts:189

    // trackpad. Ignore those here; explicit zoom is Cmd+wheel or the host
    // toolbar.
    let isGesturing = false;
    const onGestureStart = (e) => { e.preventDefault(); e.stopPropagation(); isGesturing = true; };
    const onGestureChange = (e) => {
      e.preventDefault();
      e.stopPropagation();
    };
    const onGestureEnd = (e) => { e.preventDefault(); e.stopPropagation(); isGesturing = false; };`);
  return { result, wheelMatched, gestureMatched };
}

function readCentralDirectory(zip: Buffer): ZipEntry[] {
  const eocdOffset = findEndOfCentralDirectory(zip);
  const entryCount = zip.readUInt16LE(eocdOffset + 10);
  const centralSize = zip.readUInt32LE(eocdOffset + 12);
  const centralOffset = zip.readUInt32LE(eocdOffset + 16);
  if (centralOffset + centralSize > zip.length) {
    throw new Error('invalid zip central directory');
  }

  const entries: ZipEntry[] = [];
  let offset = centralOffset;
  for (let i = 0; i < entryCount; i += 1) {
    if (zip.readUInt32LE(offset) !== CENTRAL_SIG) {
      throw new Error('invalid zip central directory entry');
    }
    const flags = zip.readUInt16LE(offset + 8);
    const method = zip.readUInt16LE(offset + 10);
    const compressedSize = zip.readUInt32LE(offset + 20);
    const uncompressedSize = zip.readUInt32LE(offset + 24);
    const nameLen = zip.readUInt16LE(offset + 28);
    const extraLen = zip.readUInt16LE(offset + 30);
    const commentLen = zip.readUInt16LE(offset + 32);
    const localOffset = zip.readUInt32LE(offset + 42);
    const name = zip.slice(offset + 46, offset + 46 + nameLen).toString('utf8');
    if ((flags & 1) !== 0) throw new Error('encrypted zip entries are not supported');

View on GitHub (pinned to 5be4028344)

Solutions

  1. Re-obtain the zip from the original source to rule out truncation during transfer.
  2. Verify integrity with a standard unzip tool; if it fails to list contents, the archive is corrupt.
  3. If you produce the archive, ensure the writer flushes the full file and writes a consistent EOCD.
Defensive patterns

Strategy: validation

Validate before calling

const eocdOffset = findEndOfCentralDirectory(zip);
const centralSize = zip.readUInt32LE(eocdOffset + 12);
const centralOffset = zip.readUInt32LE(eocdOffset + 16);
if (centralOffset + centralSize > zip.length) {
  throw new Error('archive central directory extends past end of file; archive is truncated');
}

Try / catch

try {
  await importClaudeDesignZip(zipPath, projectDir);
} catch (err) {
  if (err instanceof Error && err.message === 'invalid zip central directory') {
    // re-obtain the archive; it is truncated or corrupt
  }
  throw err;
}

Prevention

When it happens

Trigger: A zip whose EOCD record advertises a central directory offset/size that extends beyond the file bytes held in memory — e.g. a partially downloaded archive, a truncated file, or a corrupt/truncated central directory.

Common situations: The zip was truncated during download or transfer. Disk write was interrupted. A buggy producer wrote an inconsistent EOCD. An attacker crafted a misleading EOCD.

Related errors


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/0ed8899dfbd374e4. Report an issue: GitHub.