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
- Simplify or bound the expression (remove unbounded loops, shrink data)
- Move heavy work out of the expression into a script step
- 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
- Never put while(true)/heavy loops in simple expressions
- Bound input sizes before evaluation
- Raise EVAL_TIMEOUT_MS only with justification
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
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- The expression evaluation `{expr}` took too long to execute
- Timed out waiting for flow ${id} to complete
- Timed out waiting for job ${id}
- Timed out waiting for job ${id} to complete
- Giving up polling job ${jobId} after ${MAX_CONSECUTIVE_POLL_
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/305894698e28fbe3.
Report an issue: GitHub.