Decipher Groovy errors
When you develop Groovy queries, you will eventually encounter errors. This guide teaches you how to read Groovy error output so you can find and fix problems quickly.
The basics
Errors in Groovy fall into two categories:
- Compilation errors occur before execution, when the Groovy compiler finds invalid syntax.
- Runtime exceptions are unexpected events that occur while code is executing.
Unlike Python errors—which have a structured Type: / Value: / Line: format—Groovy errors appear as Java exception stack traces. When one exception causes another, they chain together using Caused by: lines. The root cause is always the last Caused by: entry in the chain.
As an example, let's execute a line of code with a syntax error:
This produces output like this:
For compilation errors, Deephaven wraps the error in a GroovyExceptionWrapper. The key information is at the top of the output:
The line and column numbers show where the syntax problem is.
Note
Each time you execute code in the Deephaven console, it is compiled into a new class named Script_N, where N increments with each execution. You will see Script_N.groovy references in stack traces wherever your code appears. The line number within Script_N.groovy may be offset from what you see in the editor because Deephaven prepends some preamble (imports and setup) before compiling your script.
The remainder of the trace shows Groovy compiler and Deephaven session infrastructure frames. These can generally be ignored — the useful signal is in the error message itself.
Create your own exception
Now let's look at a runtime exception. Here, a closure f throws when its argument is 10 or 20:
Click to see the full stack trace.
Deephaven wraps script-level exceptions in a java.lang.RuntimeException via wrapAndRewriteStackTrace. To find the actual error, follow the Caused by: chain.
Tip
When reading a Groovy stack trace, start at the last Caused by: entry and work back toward the top. That is where the root cause is.
Within the Caused by: block, look for lines that reference io.deephaven.dynamic.Script_N.groovy. These point to your code:
Note
Groovy closures appear in stack traces as Script_N$_run_closureM, where M is a number based on the closure's position in the script. This is how the Groovy compiler names anonymous closures internally. A named method would appear as Script_N.methodName. For more on closures, see Groovy closures.
Now let's add another level of depth. The closure g calls f and accumulates the results:
Click to see the full stack trace.
Because f and g were defined in separate console executions, they live in different compiled classes: Script_6 and Script_7. The trace crosses those boundaries, showing the full call chain from the root cause upward:
Exceptions in Deephaven tables
Closures can be used inside table formulas. When they throw, the error becomes more complex. In this example, f is applied as a column formula on a static table:
Click to see the full stack trace.
Working through the Caused by: chain from the bottom:
java.lang.Exception: oops 1— the root cause, thrown inside the closuref.FormulaEvaluationException: In formula: Y = f(X)— the query engine detected a failure while evaluating that formula.TableInitializationException: Error while initializing Update([Y])— the table failed to initialize because of the above.
The line Script_8.groovy:3 tells you which line in your code triggered the failing update call.
Ticking tables behave differently. When a formula is applied to a ticking table, it is evaluated for existing rows at creation time, then again for every new row as it arrives. Errors may appear well after the table is open and apparently working.
Click to see the full stack trace.
Several things differ from the static table error:
- Thread prefix:
aph-updateExecutor-Ninstead ofr-Scheduler-Serial-1. The error occurred on a background update thread, not the session thread. - Log source:
.AsyncClientErrorNotifier | Error in table update:instead of.c.ConsoleServiceGrpcImpl | Error running script. - No
TableInitializationException: The trace starts directly withFormulaEvaluationException. The table was created successfully; it only failed when a new row arrived. - No script line: Because the error occurred asynchronously, there is no reference back to the line that created the table.
In addition to the server log, the Deephaven IDE marks the failed ticking table with an error indicator in the UI.
More complex example
Now let's look at a formula that references a column that doesn't exist. f1 uses column X correctly; f2 does not:
Click to see the full stack trace.
The Caused by: chain:
QueryLanguageParser$QueryLanguageParseException— the Deephaven query language parser failed. The message is specific: it shows the full expression (X * 3) and the problem (Cannot find variable or class X).FormulaCompilationException: Formula compilation error for: X * 3— the formula could not be compiled.
The script trace points to Script_11$_run_closure2 (inside f2) and Script_11.groovy:5 (the f2() call). The _run_closure2 suffix indicates this is the second closure defined in the script (f2), confirming which function caused the problem.
This is a common mistake: referencing a column name in a formula that does not exist in the table at the point the formula is evaluated. Here, f2 tries to use X in a table that has no X column.
One more complex example
In this last example, a ticking table calls Math.floorDiv, which throws when its divisor reaches zero:
The table runs without error at first. After ten seconds, when X reaches zero, the table fails.
Click to see the full stack trace.
As expected for a ticking table error, the thread is aph-updateExecutor-N and the log entry includes a UUID. There is no reference back to the original script line.
The formula string in the error is Y = Math.floorDiv((long)1, X)—preserved exactly as written. The Caused by: chain shows the root cause: ArithmeticException: / by zero, thrown by Math.floorDiv when X reached zero.
Note
Deephaven's query engine has special null-handling behavior that can affect how some arithmetic operations behave. For example, the standard / operator on integer types may return a null value rather than throwing when the divisor is zero. To reliably produce an exception on zero, use Math.floorDiv or similar explicit Java Math methods.