{"record":{"id":"e91292bb3927cc32","repo":"different-ai/openwork","slug":"archive-entry-exceeds-the-max-entry-uncompressed","errorCode":null,"errorMessage":"Archive entry exceeds the ${MAX_ENTRY_UNCOMPRESSED_BYTES}-byte limit.","messagePattern":"Archive entry exceeds the (.+?)-byte limit\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/desktop/electron/workspace-archive.mjs","lineNumber":203,"sourceCode":"}\n\nfunction listZipEntries(buffer) {\n  if (buffer.byteLength > MAX_ARCHIVE_BYTES) throw new Error(\"Workspace archive is too large.\");\n  const eocd = findEndOfCentralDirectory(buffer);\n  const count = buffer.readUInt16LE(eocd + 10);\n  if (count > MAX_ARCHIVE_ENTRIES) throw new Error(\"Workspace archive contains too many entries.\");\n  const centralOffset = buffer.readUInt32LE(eocd + 16);\n  const entries = [];\n  let totalUncompressed = 0;\n  let cursor = centralOffset;\n  for (let i = 0; i < count; i += 1) {\n    if (buffer.readUInt32LE(cursor) !== ZIP_CENTRAL_DIRECTORY_HEADER) {\n      throw new Error(\"Invalid ZIP central directory entry.\");\n    }\n    const method = buffer.readUInt16LE(cursor + 10);\n    const compressedSize = buffer.readUInt32LE(cursor + 20);\n    const uncompressedSize = buffer.readUInt32LE(cursor + 24);\n    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}.`);","sourceCodeStart":185,"sourceCodeEnd":221,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/apps/desktop/electron/workspace-archive.mjs#L185-L221","documentation":"listZipEntries parses the ZIP central directory when reading a workspace archive. It enforces a per-entry uncompressed-size cap (MAX_ENTRY_UNCOMPRESSED_BYTES = 16 MiB, read from the central directory header at offset +24). If any single entry declares an uncompressed size above that limit, parsing is aborted with this error to prevent zip bombs and memory exhaustion before any data is inflated.","triggerScenarios":"Calling importWorkspaceConfig (or listZipEntries directly) on an archive where at least one central-directory entry has an uncompressedSize field greater than 16777216 bytes.","commonSituations":"Importing a workspace archive exported from a project with very large files under .opencode/ (e.g. bundled assets, databases, video), or a hand-crafted/malicious zip bomb with a small compressed size but huge declared uncompressed size.","solutions":["Remove or exclude the oversized entry from the archive so every entry is under 16 MiB uncompressed","Re-export the workspace after moving large data files out of .opencode/ and opencode.json scope","If the file is legitimately needed, compress it into a smaller artifact or store it outside the workspace config","If the archive came from an untrusted source, treat it as hostile — do not attempt to inflate it"],"exampleFix":"// before: archive contains .opencode/model.bin (40 MB uncompressed)\n// after: exclude it or store externally\n.opencode/\n  agent.md\n  plugin.js\n# model.bin moved to external storage, archive now imports cleanly","handlingStrategy":"validation","validationCode":"import { stat } from 'node:fs/promises';\n// pre-check total and rough sizes: use `unzip -l` or a zip parser before import\n// e.g. with yauzl/fflate: ensure every entry.uncompressedSize <= 16 * 1024 * 1024","typeGuard":"function isWithinEntryLimit(entry) {\n  return typeof entry?.uncompressedSize === 'number' &&\n    entry.uncompressedSize >= 0 &&\n    entry.uncompressedSize <= 16 * 1024 * 1024;\n}","tryCatchPattern":"try {\n  await importWorkspaceConfig({ archivePath, targetDir });\n} catch (err) {\n  if (String(err.message).includes('-byte limit')) {\n    // archive entry too large: reject or ask user to trim the archive\n  } else throw err;\n}","preventionTips":["Keep .opencode/ free of large binaries and datasets","Run `unzip -l` to check per-entry uncompressed sizes before importing","Treat untrusted archives as hostile — the limit also guards zip bombs"],"tags":["zip","security","zip-bomb","limit-exceeded"],"backgroundTag":"archive-entry-size-limit","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}