{"record":{"id":"da7899b14781be77","repo":"Budibase/budibase","slug":"invalid-zip-uncompressed-contents-exceed-the-max","errorCode":null,"errorMessage":"Invalid zip - uncompressed contents exceed the maximum size","messagePattern":"Invalid zip - uncompressed contents exceed the maximum size","errorType":"http","errorClass":"BadRequestError","httpStatus":400,"severity":"error","filePath":"packages/server/src/api/controllers/static/index.ts","lineNumber":138,"sourceCode":"      return\n    }\n\n    fileCount++\n    if (fileCount > MAX_PWA_ZIP_FILE_COUNT) {\n      throw new BadRequestError(\n        `Invalid zip - too many files (max ${MAX_PWA_ZIP_FILE_COUNT})`\n      )\n    }\n\n    if (entry.uncompressedSize > MAX_PWA_ZIP_ENTRY_SIZE) {\n      throw new BadRequestError(\n        `Invalid zip - file \"${entry.fileName}\" exceeds the maximum size`\n      )\n    }\n\n    totalUncompressedSize += entry.uncompressedSize\n    if (totalUncompressedSize > MAX_PWA_ZIP_TOTAL_SIZE) {\n      throw new BadRequestError(\n        \"Invalid zip - uncompressed contents exceed the maximum size\"\n      )\n    }\n  }\n}\n\nconst listFilesRecursively = async (directory: string): Promise<string[]> => {\n  const entries = await fsp.readdir(directory, { withFileTypes: true })\n  const files = await Promise.all(\n    entries.map(async entry => {\n      const entryPath = join(directory, entry.name)\n      if (entry.isDirectory()) {\n        return await listFilesRecursively(entryPath)\n      }\n\n      if (entry.isFile()) {\n        return [entryPath]\n      }","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/api/controllers/static/index.ts#L120-L156","documentation":"Thrown by validatePWAZipEntries when the cumulative uncompressed size of all zip entries exceeds MAX_PWA_ZIP_TOTAL_SIZE (50MB). It runs after each per-file check and bounds the total extracted footprint of the PWA in storage.","triggerScenarios":"Uploading a PWA zip where the sum of all entries' uncompressedSize exceeds 50MB, even if each individual file is under 10MB.","commonSituations":"A media-heavy PWA with many images/videos, or a zip that decompresses far beyond its small compressed size (high compression ratio archive).","solutions":["Trim archive contents until total uncompressed size is under 50MB","Optimize images (WebP/lossy compression) and drop unused locales","Move large media to external hosting and reference by URL"],"exampleFix":"// before\n60 uncompressed MB of PNGs in the zip\n// after\nconverted to WebP: 40 uncompressed MB total","handlingStrategy":"validation","validationCode":"function isPwaZipTotalSizeSafe(entries, maxTotal = 50 * 1024 * 1024) {\n  return entries.reduce((sum, e) => sum + e.uncompressedSize, 0) <= maxTotal\n}","typeGuard":"const isZipEntryList = (v: unknown): v is Array<{ fileName: string; uncompressedSize: number }> =>\n  Array.isArray(v) && v.every(e => typeof e?.fileName === 'string' && typeof e?.uncompressedSize === 'number')","tryCatchPattern":"try {\n  await uploadPwaZip(zip)\n} catch (err) {\n  if (err instanceof BadRequestError && err.message.includes('uncompressed contents exceed')) {\n    // trim/optimize archive contents and retry\n  } else throw err\n}","preventionTips":["Sum uncompressed entry sizes before upload; keep under 50MB","Beware small-but-dense zips (high compression ratio) that explode on extraction","Host large media externally instead of bundling it"],"tags":["validation","pwa","upload","zip","file-size"],"backgroundTag":"zip-validation-failed","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}