Budibase/budibase · error · JsRequestTimeoutError

JS_REQUEST_TIMEOUT_ERROR

JS_REQUEST_TIMEOUT_ERROR

Error message

CPU time limit exceeded (${cpuMs}ms > ${this.isolateAccumulatedTimeout}ms)

What it means

The isolated-vm JS runner enforces a cumulative CPU-time budget per isolate. Before executing code, execute() compares the isolate's consumed cpuTime (converted to ms) against isolateAccumulatedTimeout and throws JsRequestTimeoutError (code JS_REQUEST_TIMEOUT_ERROR) when the budget is exhausted, preventing runaway scripts from consuming host CPU.

Source

Thrown at packages/server/src/jsRunner/vm/isolated-vm.ts:181

    const bsonPolyfills = loadBundle(BundleType.BSON_POLYFILLS)

    const script = this.isolate.compileScriptSync(
      `${bsonPolyfills};${bsonSource}`
    )
    script.runSync(this.vm, { timeout: this.invocationTimeout, release: false })
    new Promise(() => {
      script.release()
    })

    return this
  }

  execute(code: string): any {
    if (this.isolateAccumulatedTimeout) {
      const cpuMs = Number(this.isolate.cpuTime) / 1e6
      if (cpuMs > this.isolateAccumulatedTimeout) {
        throw new JsRequestTimeoutError(
          `CPU time limit exceeded (${cpuMs}ms > ${this.isolateAccumulatedTimeout}ms)`
        )
      }
    }

    code = `
      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(() => {

View on GitHub (pinned to a81a902e9a)

Solutions

  1. Increase the isolateAccumulatedTimeout / CPU limit configuration to a value suited to your workloads.
  2. Recycle the isolate (create a fresh one) once its CPU budget is consumed instead of reusing it.
  3. Profile and optimize the user JS executing on the isolate to reduce CPU consumption.
  4. Distribute load across multiple isolates so no single one exhausts its budget.

Example fix

// before: one shared isolate reused until budget exhausted
const runner = getSharedIsolate()
runner.execute(code) // JsRequestTimeoutError after budget used
// after: recycle isolate when CPU budget is spent
if (runner.cpuTimeMs() > runner.limitMs()) runner = createIsolate()
runner.execute(code)
Defensive patterns

Strategy: try-catch

Validate before calling

// Check remaining CPU budget before executing
const cpuMs = Number(isolate.cpuTime) / 1e6
if (cpuMs > isolateAccumulatedTimeout) {
  isolate = createNewIsolate() // recycle before execute()
}

Type guard

function isCpuTimeout(err: unknown): err is Error & { code: string } {
  return err instanceof Error && (err as { code?: string }).code === "JS_REQUEST_TIMEOUT_ERROR"
}

Try / catch

try {
  const result = isolateVm.execute(code)
} catch (err) {
  if (isCpuTimeout(err)) {
    recycleIsolate() // fresh isolate with a clean CPU budget, then retry once
  } else { throw err }
}

Prevention

When it happens

Trigger: Calling execute() on an isolate whose accumulated CPU time already exceeds isolateAccumulatedTimeout — e.g. many prior executions on the same isolate consumed the CPU budget, or the configured limit is very low so even modest scripts trip it.

Common situations: Long-lived isolate reused across many requests slowly exhausting its CPU budget; too-low timeout config in production; a single expensive script burning most of the budget so subsequent cheap calls fail; CPU throttling in containers inflating measured CPU time.

Understand the failure class

Related errors


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