nexu-io/open-design · error · Error
zip entry exceeds archive: ${entry.name}
Error message
zip entry exceeds archive: ${entry.name} What it means
readEntryBody computes bodyEnd = bodyStart + entry.compressedSize and rejects when bodyEnd > zip.length. The central directory declared a compressed size that would read past the end of the archive. Maps to HTTP 400.
Source
Thrown at apps/daemon/src/design/claude-design-import.ts:241
function findEndOfCentralDirectory(zip: Buffer): number {
const min = Math.max(0, zip.length - 0xffff - 22);
for (let i = zip.length - 22; i >= min; i -= 1) {
if (zip.readUInt32LE(i) === EOCD_SIG) return i;
}
throw new Error('invalid zip: missing central directory');
}
function readEntryBody(zip: Buffer, entry: ZipEntry): Buffer {
const offset = entry.localOffset;
if (zip.readUInt32LE(offset) !== LOCAL_SIG) {
throw new Error(`invalid zip local header: ${entry.name}`);
}
const nameLen = zip.readUInt16LE(offset + 26);
const extraLen = zip.readUInt16LE(offset + 28);
const bodyStart = offset + 30 + nameLen + extraLen;
const bodyEnd = bodyStart + entry.compressedSize;
if (bodyEnd > zip.length) throw new Error(`zip entry exceeds archive: ${entry.name}`);
const compressed = zip.slice(bodyStart, bodyEnd);
if (entry.method === 0) return Buffer.from(compressed);
// A genuinely empty deflate payload would still occupy at least the BFINAL
// marker; an entirely missing payload cannot be inflated, so treat it as
// empty rather than handing a zero-length buffer to zlib.
if (compressed.length === 0) return Buffer.alloc(0);
// When the central directory advertises 0 (streaming zips with data
// descriptors), fall back to the per-file ceiling so legitimate non-empty
// payloads decode instead of being silently truncated. The post-decode
// checks in the caller enforce MAX_FILE_BYTES and total-bytes limits.
const cap = entry.uncompressedSize > 0 ? entry.uncompressedSize : MAX_FILE_BYTES;
return inflateRawSync(compressed, { maxOutputLength: cap });
}
function sanitizeZipPath(name: string): string {
if (name.includes('\0')) throw new Error('invalid zip file name');
if (/^[A-Za-z]:/.test(name) || name.startsWith('/')) {
throw new Error('absolute zip paths are not allowed');View on GitHub (pinned to 5be4028344)
Solutions
- Re-download or re-export the zip fully and verify the byte size matches the source.
- Retry the upload over a stable connection.
- Run `unzip -t file.zip` locally to confirm integrity before uploading.
Defensive patterns
Strategy: validation
Validate before calling
// Compare received file size to the source and run an integrity test.
import { statSync } from 'node:fs';
import { execFileSync } from 'node:child_process';
function zipIntact(file: string): boolean {
if (statSync(file).size === 0) return false;
try { execFileSync('unzip', ['-tqq', file], { stdio: 'ignore' }); return true; }
catch { return false; }
} Try / catch
try {
await importClaudeDesignZip(zipPath, projectDir);
} catch (err) {
if (String(err).includes('exceeds archive')) {
return res.status(400).json({ error: 'zip is truncated; re-download fully' });
}
throw err;
} Prevention
- Verify uploaded byte size equals the source size before importing.
- Use stable connections for large uploads; retry on truncation.
- Run `unzip -t` to detect size/offset inconsistencies.
When it happens
Trigger: A central-directory entry whose compressedSize field points beyond the archive boundary: a truncated archive, a central directory copied from a larger original, or a data-descriptor/streaming zip whose sizes are inconsistent with the buffer.
Common situations: Partial download of the zip; truncation during multipart upload; archive truncated by a proxy/CDN with a size cap; an interrupted `zip` operation.
Related errors
- zip entry size mismatch: ${relPath}
- zip contains no files
- zip does not contain an HTML file
- invalid zip central directory
- invalid zip central directory entry
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/5438723b5f83f868.
Report an issue: GitHub.