Budibase/budibase · error · UserScriptError
USER_SCRIPT_ERROR
USER_SCRIPT_ERROR
Error message
error while running user-supplied JavaScript: ${userScriptError.toString()} What it means
The isolated-vm JS runner wraps user-supplied JavaScript in a try/catch inside the V8 isolate. Any error the user's script throws (or which comes from the script's own logic/callbacks) is captured and re-thrown on the host side as a UserScriptError after execution, distinguishing user mistakes from host infrastructure failures.
Source
Thrown at packages/server/src/jsRunner/vm/isolated-vm.ts:206
try {
results = {}
results['${this.runResultKey}']=${this.codeWrapper(code)}
} catch (e) {
results['${this.runErrorKey}']=e
}
`
const script = this.isolate.compileScriptSync(code)
script.runSync(this.vm, { timeout: this.invocationTimeout, release: false })
new Promise(() => {
script.release()
})
// We can't rely on the script run result as it will not work for non-transferable values
const result = this.getFromContext(this.resultKey)
if (result[this.runErrorKey]) {
throw new UserScriptError(result[this.runErrorKey])
}
return result[this.runResultKey]
}
close(): void {
this.vm.release()
this.isolate.dispose()
}
private registerCallbacks(functions: Record<string, any>) {
const libId = crypto.randomUUID().replace(/-/g, "")
const x: Record<string, string> = {}
for (const [funcName, func] of Object.entries(functions)) {
const key = `f${libId}${funcName}cb`
x[funcName] = key
this.addToContext({View on GitHub (pinned to a81a902e9a)
Solutions
- Read the embedded userScriptError message - it is the original error from inside the sandbox; fix the user JS accordingly.
- Verify all referenced variables/fields exist and are defined before the script runs (null-check row/data inputs).
- Test the snippet in a plain Node REPL with mocked inputs to reproduce the error outside the isolate.
- If a registered callback throws, fix the callback implementation on the host side rather than the script.
Example fix
// before return data.rows[0].value * 2 // after return data.rows && data.rows[0] ? data.rows[0].value * 2 : 0
Defensive patterns
Strategy: try-catch
Validate before calling
if (typeof userCode !== "string" || !userCode.trim()) throw new Error("Empty script") Type guard
const isUserScriptError = (e: unknown): e is UserScriptError => e instanceof UserScriptError
Try / catch
try {
const result = runner.execute(code)
} catch (e) {
if (e instanceof UserScriptError) {
// surface e.message to the user as a script problem, not a server fault
} else {
throw e
}
} Prevention
- Null-check all input data referenced in the script before execution
- Keep scripts trivially simple; move complex logic into server-side helpers
- Test snippets outside the sandbox with mocked inputs
- Log the full UserScriptError message - it contains the original sandbox error
When it happens
Trigger: Calling execute(code) where the user JavaScript throws at runtime: undefined variable access, type errors, a registered callback function rejects/throws, or bson conversion fails. The isolate itself ran fine; the script errored.
Common situations: Builders write JS bindings in automations/queries that reference missing row fields, call helper functions with wrong arguments, or use APIs not available in the sandbox. CPU-time limit breaches throw a different error (JsRequestTimeoutError), not this one.
Related errors
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/af5b644d5c78c193.
Report an issue: GitHub.