{"record":{"id":"234bdec3b3f717b8","repo":"Budibase/budibase","slug":"either-file-or-key-is-required","errorCode":null,"errorMessage":"Either file or key is required.","messagePattern":"Either file or key is required\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/server/src/sdk/workspace/backups/imports.ts","lineNumber":144,"sourceCode":"}\n\n/**\n * This function manages temporary template files which are stored by Koa.\n * @param template The template object retrieved from the Koa context object.\n * @returns Returns a fs read stream which can be loaded into the database.\n */\nasync function getTemplateStream(template: TemplateType) {\n  if (template.file && template.file.type !== \"text/plain\") {\n    throw new Error(\"Cannot import a non-text based file.\")\n  }\n  if (template.file) {\n    return fs.createReadStream(template.file.path)\n  } else if (template.key) {\n    const [type, name] = template.key.split(\"/\")\n    const tmpPath = await downloadTemplate(type, name)\n    return fs.createReadStream(join(tmpPath, name, \"db\", \"dump.txt\"))\n  } else {\n    throw new Error(\"Either file or key is required.\")\n  }\n}\n\nexport async function untarFile(file: { path: string }) {\n  const tmpPath = join(budibaseTempDir(), uuid())\n  await fsp.mkdir(tmpPath)\n  // extract the tarball\n  await tar.extract({\n    cwd: tmpPath,\n    file: file.path,\n  })\n  return tmpPath\n}\n\nexport async function decryptFiles(path: string, password: string) {\n  try {\n    const processDirectory = async (dirPath: string) => {\n      for (let file of await fsp.readdir(dirPath)) {","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/sdk/workspace/backups/imports.ts#L126-L162","documentation":"getTemplateStream requires the template to supply either an uploaded `file` or an object-store `key`; when both are absent it has nothing to stream from and throws. It is the terminal else of the file/key branch in importApp's stream resolution.","triggerScenarios":"importApp called with a template object containing neither template.file nor template.key — e.g. multipart upload missing the file field, or a programmatic import constructing { name } only.","commonSituations":"API clients forgetting the file form field in multipart requests; templates referencing an object-store key that was never uploaded and code paths that strip the key; scripts importing apps without attaching the export payload.","solutions":["Attach the export file to the multipart request under the expected field name","Supply template.key pointing to a valid object-store template (type/name)","Validate the request body client-side before calling importApp","Check the downloadTemplate path if relying on key — ensure the template exists in the store"],"exampleFix":"// before\nawait importApp({ name: \"my-app\" } as any) // throws\n// after\nawait importApp({ name: \"my-app\", file: { path: tmpDumpPath, type: \"text/plain\", password: \"\" } })","handlingStrategy":"validation","validationCode":"if (!template.file && !template.key) {\n  throw new Error(\"Provide either template.file or template.key\")\n}","typeGuard":"function hasImportSource<T extends { file?: unknown; key?: string }>(t: T): t is T & ({ file: object } | { key: string }) {\n  return !!t.file || !!t.key\n}","tryCatchPattern":"try {\n  await importApp(template)\n} catch (err: any) {\n  if (err.message.includes(\"file or key is required\")) {\n    // attach the export payload and retry\n  } else {\n    throw err\n  }\n}","preventionTips":["Verify multipart field names match the API contract","Use typed template constructors to guarantee a source is present","Confirm object-store keys exist before referencing them"],"tags":["import","missing-parameter","validation"],"backgroundTag":"missing-required-parameter","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}