windmill-labs/windmill · error

The expression evaluation took too long to execute (>{EVAL_T

Error message

The expression evaluation took too long to execute (>{EVAL_TIMEOUT_MS}ms)

What it means

The eval_simple_js path (used by windmill-api for batch rerun) enforces the same EVAL_TIMEOUT_MS wall-clock limit as transform evals. When the spawned evaluation does not finish in time, the timed join returns this error and the underlying task is dropped.

Source

Thrown at backend/windmill-jseval/src/lib.rs:962

// ── eval_simple_js for windmill-api batch rerun ──────────────────────

#[cfg(feature = "quickjs")]
pub async fn eval_simple_js(
    expr: String,
    globals: HashMap<String, serde_json::Value>,
) -> anyhow::Result<Box<RawValue>> {
    let memory_limit = EVAL_SIMPLE_JS_MEMORY_LIMIT_BYTES;
    let handle = tokio::runtime::Handle::current();
    tokio::time::timeout(
        std::time::Duration::from_millis(EVAL_TIMEOUT_MS),
        tokio::task::spawn_blocking(move || {
            handle
                .block_on(async move { eval_simple_js_inner(&expr, &globals, memory_limit).await })
        }),
    )
    .await
    .map_err(|_| {
        anyhow::anyhow!("The expression evaluation took too long to execute (>{EVAL_TIMEOUT_MS}ms)")
    })??
}

#[cfg(feature = "quickjs")]
async fn eval_simple_js_inner(
    expr: &str,
    globals: &HashMap<String, serde_json::Value>,
    memory_limit: usize,
) -> anyhow::Result<Box<RawValue>> {
    let runtime = AsyncRuntime::new()?;
    runtime.set_memory_limit(memory_limit).await;
    let context = AsyncContext::full(&runtime).await?;

    async_with!(context => |ctx| {
        let js_globals = ctx.globals();

        // Set up each named global
        for (name, value) in globals {

View on GitHub (pinned to e474e8803c)

Solutions

  1. Simplify or bound the expression (remove unbounded loops, shrink data)
  2. Move heavy work out of the expression into a script step
  3. Raise EVAL_TIMEOUT_MS if the evaluation is legitimately long but terminates

Example fix

// before
let s = ''; for (let i = 0; i < 1e9; i++) s += i;
// after
items.reduce((acc, x) => acc + x.value, 0)
Defensive patterns

Strategy: try-catch

Validate before calling

// keep expressions bounded; reject obviously heavy code before eval
if (/while\s*\(\s*true/.test(expr)) throw new Error('unbounded loop in expression');

Try / catch

try {
  const result = await evalSimpleJs(expr, globals);
} catch (e) {
  if (String(e).includes('took too long to execute')) {
    // rewrite expression or move work to a script step
  }
  throw e;
}

Prevention

When it happens

Trigger: A simple JS expression in a batch rerun containing a long/infinite loop or operating on data large enough that evaluation exceeds the timeout window.

Common situations: Re-running a historical run whose expression now processes bigger data; an expression edited to include an accidental while(true); very slow string processing on large payloads.

Understand the failure class

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/305894698e28fbe3. Report an issue: GitHub.