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
- Upload the archive via the plugin management endpoint/upload flow instead of app import
- Install the plugin in the builder (Plugins section) and import an actual app export separately
- 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
- Inspect archives before import to classify them
- Separate plugin and app artifact pipelines
- Use the builder's Plugins section for plugin installation
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
- Unable to determine delimiter
- Unknown plugin type - check schema.json: ${schema.type}
- TableImportError(errors)
- Please run in a plugin directory - must contain package.json
- Please run in a plugin directory - must contain schema.json
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/370186606c920693.
Report an issue: GitHub.