{"record":{"id":"f3941bc80f008fb1","repo":"Budibase/budibase","slug":"js-invalid-message","errorCode":null,"errorMessage":"JS invalid: ${message}","messagePattern":"JS invalid: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/pro/src/sdk/plugins/index.ts","lineNumber":52,"sourceCode":"  const files = await objectStore.uploadDirectory(\n    objectStore.ObjectStoreBuckets.PLUGINS,\n    directory,\n    bucketPath\n  )\n  const jsFile = files.find((file: any) => file.name.endsWith(\".js\"))\n  const iconFile = files.find((file: any) => file.name.endsWith(\".svg\"))\n  if (!jsFile) {\n    throw new Error(`Plugin missing .js file.`)\n  }\n  // validate the JS for a datasource\n  if (metadata.schema.type === PluginType.DATASOURCE) {\n    const js = loadJSFile(directory, jsFile.name)\n    const isolate = new ivm.Isolate({ memoryLimit: 8 })\n    try {\n      isolate.compileScriptSync(Module.wrap(js), { filename: jsFile.name })\n    } catch (err: any) {\n      const message = err?.message ? err.message : JSON.stringify(err)\n      throw new Error(`JS invalid: ${message}`)\n    } finally {\n      isolate.dispose()\n    }\n  }\n  const iconFileName = iconFile ? iconFile.name : null\n  const pluginId = dbCore.generatePluginID(name)\n\n  // overwrite existing docs entirely if they exist\n  let rev\n  try {\n    const existing = await db.get<Plugin>(pluginId)\n    rev = existing._rev\n  } catch (err) {\n    rev = undefined\n  }\n  let doc: Plugin = {\n    _id: pluginId,\n    _rev: rev,","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/pro/src/sdk/plugins/index.ts#L34-L70","documentation":"After finding a datasource plugin's .js bundle, storePlugin compiles it inside an isolated-vm Isolate (compileScriptSync on Module.wrap(js)) as a sanity check. If the script fails to compile — a syntax error, unsupported syntax for the isolate, or a non-Error thrown value — the original error's message is rethrown prefixed with 'JS invalid: '. This stops broken plugin code from being stored and executed later.","triggerScenarios":"Uploading a datasource plugin whose .js bundle contains a JavaScript syntax error; a bundle transpiled with syntax the isolate's V8 version cannot parse; loadJSFile returning garbage (e.g. minified HTML error page saved as .js).","commonSituations":"Plugin build produced a partial/corrupt bundle; author hand-edited the bundle; bundler target mismatch with the embedded V8 (isolated-vm) version; the .js file is actually a download error page from a proxy.","solutions":["Open the reported file and fix the syntax error at the position given in the wrapped message","Rebuild the plugin bundle with a bundler target compatible with the runtime's V8 version","Validate the bundle locally with node --check before zipping and uploading","Ensure the .js file is real JavaScript (not an HTML error page or truncated download) and re-upload"],"exampleFix":"// before: uploading a bundle with a syntax error (truncated file)\nexport default class MyDatasource { connect() {\n// after: verify before upload\n// after fixing\nexport default class MyDatasource { connect() { /* ... */ } }","handlingStrategy":"validation","validationCode":"// validate locally before uploading\nrequire(\"child_process\").execSync(`node --check ${pluginJsFile}`)","typeGuard":null,"tryCatchPattern":"try {\n  storePluginFile(plugin)\n} catch (err: any) {\n  if (String(err.message).startsWith(\"JS invalid:\")) {\n    console.error(\"Plugin bundle failed isolate compile:\", err.message)\n  } else { throw err }\n}","preventionTips":["Run node --check on the bundle before upload","Keep bundler target compatible with the runtime's V8","Never hand-edit generated plugin bundles","Confirm downloaded .js files are not HTML error pages"],"tags":["plugins","javascript","isolated-vm","syntax"],"backgroundTag":"plugin-js-invalid","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}