{"record":{"id":"9c611314a16e1a42","repo":"Budibase/budibase","slug":"file-is-not-valid-cannot-upload","errorCode":null,"errorMessage":"File is not valid - cannot upload.","messagePattern":"File is not valid - cannot upload\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/server/src/api/controllers/plugin/file.ts","lineNumber":14,"sourceCode":"import {\n  createTempFolder,\n  deleteFolderFileSystem,\n  getPluginMetadata,\n  extractTarball,\n} from \"../../../utilities/fileSystem\"\nimport { KoaFile } from \"@budibase/types\"\n\nexport async function fileUpload(file: KoaFile) {\n  const filename = file.originalFilename\n  const filePath = file.filepath\n\n  if (!filename || !filePath) {\n    throw new Error(\"File is not valid - cannot upload.\")\n  }\n  if (!filename.endsWith(\".tar.gz\")) {\n    throw new Error(\"Plugin must be compressed into a gzipped tarball.\")\n  }\n  const path = createTempFolder(filename.split(\".tar.gz\")[0])\n\n  try {\n    await extractTarball(filePath, path)\n    return await getPluginMetadata(path)\n  } catch (err) {\n    deleteFolderFileSystem(path)\n    throw err\n  }\n}\n","sourceCodeStart":1,"sourceCodeEnd":29,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/api/controllers/plugin/file.ts#L1-L29","documentation":"fileUpload validates the uploaded plugin file before extracting: a missing originalFilename or filepath (the multipart fields populated by the upload middleware) means the file never made it through intact, so this Error is thrown. It is a guard against empty or malformed uploads.","triggerScenarios":"POSTing to the plugin upload endpoint without a valid multipart file field; a 0-byte or stream-truncated upload where koa-body/formidable doesn't populate originalFilename/filepath; uploading with the wrong form field name.","commonSituations":"curl calls missing -F file=@...; proxy/nginx stripping or buffering the multipart body; Content-Type not multipart/form-data; client aborting mid-upload; oversized uploads rejected upstream leaving empty temp files.","solutions":["Upload with a proper multipart form including a file field (curl -F \"file=@plugin.tar.gz\")","Ensure Content-Type is multipart/form-data and the field name matches the server's expectation","Check proxy (nginx client_max_body_size) isn't truncating large tarballs","Retry the upload; verify the file exists and is non-empty before sending"],"exampleFix":"// before\ncurl -X POST https://example.com/api/plugins -H \"Content-Type: application/json\" -d '{}'\n// after\ncurl -X POST https://example.com/api/plugins -F \"file=@./plugin.tar.gz\"","handlingStrategy":"validation","validationCode":"const form = new FormData()\nconst file = fs.readFileSync(\"./plugin.tar.gz\")\nif (!file.length) throw new Error(\"file is empty\")\nform.append(\"file\", new Blob([file]), \"plugin.tar.gz\")\nawait api.post(\"/api/plugins\", form)\n","typeGuard":"const isUploadable = (f: File | null): f is File =>\n  f !== null && f.size > 0 && f.name.length > 0\n","tryCatchPattern":"try {\n  await api.post(\"/api/plugins\", form)\n} catch (err) {\n  if (err.message === \"File is not valid - cannot upload.\") {\n    console.error(\"Upload rejected: check multipart field name and file presence\")\n  } else { throw err }\n}\n","preventionTips":["Always send multipart/form-data with the correct file field name","Verify the file exists and is non-empty before uploading","Check proxy body-size limits for large tarballs","Don't set Content-Type manually when using FormData"],"tags":["upload","plugin","multipart","validation"],"backgroundTag":"invalid-file-upload","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}