{"record":{"id":"3da5ad849a752e07","repo":"Budibase/budibase","slug":"project-package-is-invalid","errorCode":null,"errorMessage":"Project package is invalid.","messagePattern":"Project package is invalid\\.","errorType":"http","errorClass":"HTTPError","httpStatus":400,"severity":"error","filePath":"packages/server/src/sdk/workspace/projects/backups/imports.ts","lineNumber":230,"sourceCode":"      files.push(fullPath)\n    }\n  }\n\n  return files\n}\n\nconst validateProjectPackageBeforeExtraction = async (file: {\n  path: string\n}) => {\n  const archiveHeader = new Uint8Array(2)\n  const archiveFile = await fsp.open(file.path, \"r\")\n  try {\n    await archiveFile.read(archiveHeader, 0, archiveHeader.length, 0)\n  } finally {\n    await archiveFile.close()\n  }\n  if (archiveHeader[0] !== 0x1f || archiveHeader[1] !== 0x8b) {\n    throw new HTTPError(\"Project package is invalid.\", 400)\n  }\n\n  const totals = { files: 0, bytes: 0 }\n  const stream = fs.createReadStream(file.path)\n  let entries = 0\n\n  let validationError: HTTPError | undefined\n  const fail = (error: HTTPError) => {\n    validationError = error\n    stream.destroy(error)\n  }\n\n  const parser = tar.list({\n    onReadEntry: (entry: ProjectPackageTarEntry) => {\n      if (validationError) {\n        return\n      }\n      entries += 1","sourceCodeStart":212,"sourceCodeEnd":248,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/sdk/workspace/projects/backups/imports.ts#L212-L248","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: uncompressed tar uploaded as project package\ntar -cf project.tar ./app && curl -F file=@project.tar ...\n// after: gzip-compressed package\ntar -czf project.tar.gz ./app && curl -F file=@project.tar.gz ...","handlingStrategy":"validation","validationCode":"import { openSync, readSync, closeSync } from \"fs\"\n\nfunction isGzipFile(path: string): boolean {\n  const fd = openSync(path, \"r\")\n  try {\n    const header = Buffer.alloc(2)\n    readSync(fd, header, 0, 2, 0)\n    return header[0] === 0x1f && header[1] === 0x8b\n  } finally {\n    closeSync(fd)\n  }\n}\n\nif (!isGzipFile(file.path)) throw new Error(\"Not a gzip archive\")","typeGuard":null,"tryCatchPattern":"try {\n  await api.importProjectPackage(file)\n} catch (err) {\n  if (err?.status === 400 && err?.message === \"Project package is invalid.\") {\n    // verify the upload is a gzip .tar.gz and re-export if needed\n  } else throw err\n}","preventionTips":["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"],"tags":["http-400","gzip","magic-bytes","file-validation"],"backgroundTag":"invalid-archive-format","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}