Budibase/budibase · error · HTTPError
Project package is invalid.
Error message
Project package is invalid.
What it means
validateProjectPackageBeforeExtraction reads the first two bytes of the uploaded file and verifies the gzip magic number (0x1f 0x8b). If the header bytes do not match, the file is not a gzip archive and this HTTPError (400) is thrown before any extraction is attempted.
Source
Thrown at packages/server/src/sdk/workspace/projects/backups/imports.ts:230
files.push(fullPath)
}
}
return files
}
const validateProjectPackageBeforeExtraction = async (file: {
path: string
}) => {
const archiveHeader = new Uint8Array(2)
const archiveFile = await fsp.open(file.path, "r")
try {
await archiveFile.read(archiveHeader, 0, archiveHeader.length, 0)
} finally {
await archiveFile.close()
}
if (archiveHeader[0] !== 0x1f || archiveHeader[1] !== 0x8b) {
throw new HTTPError("Project package is invalid.", 400)
}
const totals = { files: 0, bytes: 0 }
const stream = fs.createReadStream(file.path)
let entries = 0
let validationError: HTTPError | undefined
const fail = (error: HTTPError) => {
validationError = error
stream.destroy(error)
}
const parser = tar.list({
onReadEntry: (entry: ProjectPackageTarEntry) => {
if (validationError) {
return
}
entries += 1View on GitHub (pinned to a81a902e9a)
Solutions
- Ensure the package is gzip-compressed: create it with `tar -czf project.tar.gz <dir>` and verify with `file project.tar.gz` (should report 'gzip compressed data').
- Re-download/re-export the package; compare checksums to rule out truncation or corruption in transit.
- Check that the client posts the correct file field and is not sending a different export artifact.
- If exporting programmatically, confirm gzip compression step runs before upload.
Example fix
// before: uncompressed tar uploaded as project package tar -cf project.tar ./app && curl -F file=@project.tar ... // after: gzip-compressed package tar -czf project.tar.gz ./app && curl -F file=@project.tar.gz ...
Defensive patterns
Strategy: validation
Validate before calling
import { openSync, readSync, closeSync } from "fs"
function isGzipFile(path: string): boolean {
const fd = openSync(path, "r")
try {
const header = Buffer.alloc(2)
readSync(fd, header, 0, 2, 0)
return header[0] === 0x1f && header[1] === 0x8b
} finally {
closeSync(fd)
}
}
if (!isGzipFile(file.path)) throw new Error("Not a gzip archive") Try / catch
try {
await api.importProjectPackage(file)
} catch (err) {
if (err?.status === 400 && err?.message === "Project package is invalid.") {
// verify the upload is a gzip .tar.gz and re-export if needed
} else throw err
} Prevention
- Always create packages with `tar -czf` and verify with `file` or `gzip -t` before upload
- Verify upload checksums to detect truncated or corrupted transfers
- Don't rename other formats (zip, plain tar) to .tar.gz
- Check the magic bytes client-side before uploading
When it happens
Trigger: Calling the project import/restore API with a file whose first two bytes are not 0x1f,0x8b — e.g. a plain (uncompressed) tar, a zip file, a JSON export, a truncated upload, or any non-archive file.
Common situations: Uploading an uncompressed .tar instead of .tar.gz; a proxy or browser mangled/truncated the upload; user renamed a zip to .tar.gz; uploading an old export format (e.g. plain JSON app export) to the new package import endpoint.
Related errors
- Invalid bookmark query
- Invalid limit query
- Limit query must be between 1 and 100
- Invalid ${queryName} query
- Invalid environment query
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/3da5ad849a752e07.
Report an issue: GitHub.