Budibase/budibase · error

Supplied file is a plugin - cannot import as app.

Error message

Supplied file is a plugin - cannot import as app.

What it means

importApp detects plugin.min.js in the extracted package and refuses to import it as an app. Plugins (custom components/datasources) are installed through the plugin upload flow, not the app import flow, so this guard prevents misfiling a plugin bundle as an application.

Source

Thrown at packages/server/src/sdk/workspace/backups/imports.ts:279

  const objectStoreProdAppId = dbCore.getProdWorkspaceID(objectStoreWorkspaceId)
  let dbStream: fs.ReadStream
  const isTar = template.file && template?.file?.type?.endsWith("gzip")
  const isDirectory =
    template.file && (await fsp.lstat(template.file.path)).isDirectory()
  let tmpPath: string | undefined = undefined
  if (template.file && (isTar || isDirectory)) {
    tmpPath = isTar ? await untarFile(template.file) : template.file.path
    if (isTar && template.file.password) {
      await decryptFiles(tmpPath, template.file.password)
    }
    const contents = await fsp.readdir(tmpPath)
    const stillEncrypted = !!contents.find(name => name.endsWith(".enc"))
    if (stillEncrypted) {
      throw new Error("Files are encrypted but no password has been supplied.")
    }
    const isPlugin = !!contents.find(name => name === "plugin.min.js")
    if (isPlugin) {
      throw new Error("Supplied file is a plugin - cannot import as app.")
    }
    const isInvalid = !contents.find(name => name === DB_EXPORT_FILE)
    if (isInvalid) {
      throw new Error(
        "App export does not appear to be valid - no DB file found."
      )
    }
    // have to handle object import
    if (importOpts.importObjStoreContents) {
      const promises = []
      const excludedFiles = [GLOBAL_DB_EXPORT_FILE, DB_EXPORT_FILE]

      for (let filename of contents) {
        const path = join(tmpPath, filename)
        if (excludedFiles.includes(filename)) {
          continue
        }
        filename = join(objectStoreProdAppId, filename)

View on GitHub (pinned to a81a902e9a)

Solutions

  1. Upload the archive via the plugin management endpoint/upload flow instead of app import
  2. Install the plugin in the builder (Plugins section) and import an actual app export separately
  3. Confirm the archive contents — plugin.min.js means it is a plugin, not an app export

Example fix

// before
await importApp(pluginArchivePath) // throws
// after
await fetch("/api/plugins", { method: "POST", body: pluginFormData }) // plugin upload flow
Defensive patterns

Strategy: validation

Validate before calling

const listing = execSync(`tar -tzf ${archivePath}`).toString()
if (listing.includes("plugin.min.js")) {
  throw new Error("Use the plugin upload flow, not app import")
}

Try / catch

try {
  await importApp(template)
} catch (err: any) {
  if (err.message.includes("Supplied file is a plugin")) {
    // redirect to plugin install endpoint
  } else {
    throw err
  }
}

Prevention

When it happens

Trigger: Uploading a plugin archive (containing plugin.min.js) to the app import endpoint (importApp), e.g. selecting a plugin zip in an import dialog that expects an app export.

Common situations: Users confusing the plugin upload and app import screens; scripts pointing at the wrong endpoint; an export from a plugin-template repository being imported as an app.

Related errors


AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29). Data as JSON: /api/errors/370186606c920693. Report an issue: GitHub.