From 10e443f42f2bee29a1d4ade4ffd0e213d47c5c52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Delbrayelle?= Date: Wed, 29 Apr 2026 12:48:52 +0200 Subject: [PATCH] fix: make Frame.lookup() iterative to prevent StackOverflowError MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Java does not optimize tail calls. On JVMs with smaller default thread stack sizes (e.g. Windows workers, containers with -Xss256k), deeply nested JSONata expressions cause Jsonata$Frame.lookup() to overflow the stack via tail recursion. StackOverflowError is a JVM Error — it cannot be safely caught and the entire worker thread crashes. Replace the recursive parent.lookup(name) call with an iterative loop over the scope chain. Semantics are identical; traversal order is unchanged. Eliminates all stack risk from scope chain lookup regardless of nesting depth. Fixes worker crashes reported in kestra-io/plugin-transform#79. --- src/main/java/com/dashjoin/jsonata/Jsonata.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/dashjoin/jsonata/Jsonata.java b/src/main/java/com/dashjoin/jsonata/Jsonata.java index d60bf19..047affb 100644 --- a/src/main/java/com/dashjoin/jsonata/Jsonata.java +++ b/src/main/java/com/dashjoin/jsonata/Jsonata.java @@ -89,12 +89,11 @@ public void bind(String name, JFunction function) { public void bind(String name, Fn2 lambda) { bind(name, (Object)lambda); } public Object lookup(String name) { - // Important: if we have a null value, - // return it - if (bindings.containsKey(name)) - return bindings.get(name); - if (parent!=null) - return parent.lookup(name); + // Iterative walk of the parent scope chain to avoid StackOverflowError + // on JVMs with small thread stacks (e.g. Windows workers). + for (Frame f = this; f != null; f = f.parent) { + if (f.bindings.containsKey(name)) return f.bindings.get(name); + } return null; }