Budibase/budibase · error · BadRequestError
Invalid zip - directory depth exceeds ${MAX_PWA_ZIP_DEPTH}
Error message
Invalid zip - directory depth exceeds ${MAX_PWA_ZIP_DEPTH} What it means
Thrown by validatePWAZipEntries when a PWA zip archive contains a file or directory whose path nesting is deeper than MAX_PWA_ZIP_DEPTH (10). The depth is computed by splitting entry.fileName on '/' and adjusting for trailing directory entries. This guard limits recursive extraction depth and prevents zip-bomb style deep nesting attacks.
Source
Thrown at packages/server/src/api/controllers/static/index.ts:113
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) {
throw new BadRequestError(
`Invalid zip - too many files (max ${MAX_PWA_ZIP_FILE_COUNT})`
)
}
if (entry.uncompressedSize > MAX_PWA_ZIP_ENTRY_SIZE) {
throw new BadRequestError(View on GitHub (pinned to a81a902e9a)
Solutions
- Flatten the zip so no path exceeds 10 directory levels before uploading
- Remove unneeded wrapper folders from the archive root
- Split assets across multiple uploads if the structure is inherently deep
Example fix
// before assets/js/chunks/vendor/lib/icons/16/launcher/icon.png (11 levels) // after assets/icons/launcher/icon.png (within 10 levels)
Defensive patterns
Strategy: validation
Validate before calling
function isPwaZipDepthSafe(fileNames, maxDepth = 10) {
return fileNames.every(name => {
const depth = name.split('/').filter(Boolean).length - (name.endsWith('/') ? 0 : 1)
return depth <= maxDepth
})
} Type guard
const isZipEntry = (e: unknown): e is { fileName: string; uncompressedSize: number; externalFileAttributes: number } =>
typeof e === 'object' && e !== null && typeof (e as any).fileName === 'string' && typeof (e as any).uncompressedSize === 'number' Try / catch
try {
await uploadPwaZip(zip)
} catch (err) {
if (err instanceof BadRequestError && err.message.includes('directory depth')) {
// rebuild the archive with a flattened structure
} else throw err
} Prevention
- Inspect zip entry paths before upload and flatten if deeper than 10 levels
- Avoid zipping already-nested extracted output
- Add a client-side pre-check of entry path depth with a zip library
When it happens
Trigger: Uploading a PWA zip (via processPWAZip) where any entry's path has more than 10 directory levels, e.g. 'a/b/c/d/e/f/g/h/i/j/k/icon.png'.
Common situations: Packaging a PWA from a build tool that outputs deeply nested hashed asset folders (e.g. assets/js/chunk/vendor/... chains), or re-uploading a zip that extracted inside a prior folder structure.
Related errors
- 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
- Stream to upload is invalid/undefined
- Plugin missing .js file.
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/9c6690af7efe98fd.
Report an issue: GitHub.