Budibase/budibase · error
Plugin missing .js file.
Error message
Plugin missing .js file.
What it means
When storing an uploaded plugin, storePlugin lists the files uploaded to the object store under the plugin's bucket path and searches for a file ending in .js. Budibase plugins are bundled as a single .js bundle (plus an optional .svg icon), so if no .js file is present the upload is considered malformed and rejected. This prevents persisting a plugin that could never be loaded.
Source
Thrown at packages/pro/src/sdk/plugins/index.ts:42
origin?: PluginOrigin
): Promise<Plugin> {
const db = tenancy.getGlobalDB()
const version = metadata.package.version,
name = metadata.package.name,
description = metadata.package.description,
hash = metadata.schema.hash
// first open the tarball into tmp directory
const bucketPath = objectStore.getPluginS3Dir(name)
const files = await objectStore.uploadDirectory(
objectStore.ObjectStoreBuckets.PLUGINS,
directory,
bucketPath
)
const jsFile = files.find((file: any) => file.name.endsWith(".js"))
const iconFile = files.find((file: any) => file.name.endsWith(".svg"))
if (!jsFile) {
throw new Error(`Plugin missing .js file.`)
}
// validate the JS for a datasource
if (metadata.schema.type === PluginType.DATASOURCE) {
const js = loadJSFile(directory, jsFile.name)
const isolate = new ivm.Isolate({ memoryLimit: 8 })
try {
isolate.compileScriptSync(Module.wrap(js), { filename: jsFile.name })
} catch (err: any) {
const message = err?.message ? err.message : JSON.stringify(err)
throw new Error(`JS invalid: ${message}`)
} finally {
isolate.dispose()
}
}
const iconFileName = iconFile ? iconFile.name : null
const pluginId = dbCore.generatePluginID(name)
// overwrite existing docs entirely if they existView on GitHub (pinned to a81a902e9a)
Solutions
- Rebuild the plugin (npm/yarn build in the plugin repository) so a bundled .js file is produced
- Verify the plugin archive contains the .js bundle at the top level alongside metadata.json
- Re-zip the plugin using the plugin CLI's bundling output rather than the raw source folder
- Check the object-store bucket path configuration so the file listing actually covers where the .js was uploaded
Example fix
# before: zipping the plugin source directory zip -r my-plugin.zip my-plugin/ // after: zip the built bundle output # after building the plugin, zip dist contents containing metadata.json + plugin .js zip -j my-plugin.zip dist/metadata.json dist/*.js
Defensive patterns
Strategy: validation
Validate before calling
const files = await objectStore.listBucket(pluginDir)
if (!files.some(f => f.name.endsWith(".js"))) {
throw new Error("Plugin archive must contain a compiled .js bundle")
} Prevention
- Always build the plugin before packaging
- Zip the dist output, not the source folder
- Validate archive contents (metadata.json + .js) before upload
When it happens
Trigger: Calling the plugin upload/store API with an archive that contains only metadata.json (and maybe an .svg icon) but no compiled .js bundle; uploading a plugin built with the wrong output filename or directory structure.
Common situations: A plugin author zipped the source folder instead of the built dist output; the plugin build (yarn build in the plugin repo) was never run; the archive path layout puts the .js file in a subdirectory not covered by the bucketPath listing.
Related errors
- Stream to upload is invalid/undefined
- File is not valid - cannot upload.
- Plugin must be compressed into a gzipped tarball.
- Invalid plugin URL.
- Plugin must be compressed into a gzipped tarball.
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/408f118724606ce5.
Report an issue: GitHub.