Budibase/budibase · error · BadRequestError
Invalid zip
Error message
Invalid zip
What it means
When importing a PWA zip, each entry is scanned for security hazards. The entry's external file attributes are inspected for the unix file-type bits; if the entry is a symbolic link (ZIP_SYMLINK_FILE_TYPE) the zip is rejected as a BadRequestError to prevent symlink-based path traversal / file-overwrite attacks during extraction.
Source
Thrown at packages/server/src/api/controllers/static/index.ts:106
const ZIP_SYMLINK_FILE_TYPE = 0o120000
const validatePWAZipEntries = () => {
let fileCount = 0
let totalUncompressedSize = 0
return (entry: {
fileName: string
uncompressedSize: number
externalFileAttributes: number
}) => {
// extract-zip skips these itself, so don't count them against the limits.
if (entry.fileName.startsWith("__MACOSX/")) {
return
}
const fileType = (entry.externalFileAttributes >>> 16) & ZIP_FILE_TYPE_MASK
if (fileType === ZIP_SYMLINK_FILE_TYPE) {
throw new BadRequestError(`Invalid zip`)
}
const depth =
entry.fileName.split("/").filter(Boolean).length -
(entry.fileName.endsWith("/") ? 0 : 1)
if (depth > MAX_PWA_ZIP_DEPTH) {
throw new BadRequestError(
`Invalid zip - directory depth exceeds ${MAX_PWA_ZIP_DEPTH}`
)
}
// Directory entries carry no content, only enforce the depth limit on them.
if (entry.fileName.endsWith("/")) {
return
}
fileCount++
if (fileCount > MAX_PWA_ZIP_FILE_COUNT) {View on GitHub (pinned to a81a902e9a)
Solutions
- Rebuild the zip resolving symlinks to real files (e.g. `zip -r pwa.zip . -y` without -y, or follow links during packaging).
- Exclude symlink entries when packaging: filter them out of the archive or delete them from the build output.
- Use a bundler/archiver configuration that dereferences symlinks (e.g. archiver with `follow: true`).
- Remove macOS junk like __MACOSX/ and .DS_Store and re-export the archive cleanly.
Example fix
// before (preserves symlinks)
execSync("zip -ry pwa.zip dist/")
// after (dereferences symlinks)
execSync("zip -r pwa.zip dist/") Defensive patterns
Strategy: validation
Validate before calling
// client-side pre-check: reject zips that contain symlinks before upload
// e.g. with yauzl on the producing side, or shell: `unzip -Z1 pwa.zip` combined with
// `zipinfo pwa.zip | grep '^l'` — fail fast if any symlink entries exist
if (execSync("zipinfo -1 pwa.zip | awk -F' -> ' 'NR && $2').toString().trim()) {
throw new Error("Zip contains symlinks; rebuild without them")
} Type guard
function isRegularFileEntry(entry: { externalFileAttributes: number }): boolean {
const fileType = (entry.externalFileAttributes >>> 16) & 0o170000
return fileType !== 0o120000 // S_IFLNK
} Try / catch
try {
await uploadPwaZip(zipPath)
} catch (err) {
if (err instanceof Error && err.message === "Invalid zip") {
console.error("Zip rejected: contains symlink entries — rebuild with dereferenced symlinks")
}
throw err
} Prevention
- Package build output with symlinks dereferenced (no `zip -y`).
- Strip __MACOSX/, .DS_Store and symlinked assets from archives.
- Scan archives with zipinfo for 'l' (symlink) entries in CI before upload.
- Keep packaging scripts in the repo so the same flags are used everywhere.
When it happens
Trigger: Uploading a PWA zip that contains symlink entries — commonly zips built on macOS or Linux with tools that preserve symlinks (tar->zip conversions, node_modules zips, zips containing __MACOSX metadata); validatePWAZipEntries throws at packages/server/src/api/controllers/static/index.ts:106.
Common situations: Building a PWA bundle on macOS where the archiver follows/preserves symlinks; packaging a build output directory that includes symlinked assets; zipping with `zip -y` or equivalent that stores symlinks rather than their targets.
Related errors
- Invalid zip - directory depth exceeds ${MAX_PWA_ZIP_DEPTH}
- Invalid zip - too many files (max ${MAX_PWA_ZIP_FILE_COUNT})
- Invalid zip - file "${entry.fileName}" exceeds the maximum s
- Invalid zip - uncompressed contents exceed the maximum size
- Invalid object store key: path traversal is not allowed.
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/15623821beb01c8d.
Report an issue: GitHub.