Limits and Errors
When a script fails, the platform categorizes the error, writes it into the execution logs, and returns the error message as the tool's result. This page covers the error taxonomy, how failures surface to agents and to you, what execution limits actually exist, and the workflow for debugging a misbehaving script.
Error taxonomy
Every failed execution is categorized into one of five types, based on the error the engine reported:
- Name
JS_SYNTAX_ERROR- Type
- category
- Description
The script didn't parse — the engine reported a
SyntaxError(including theawait is only valid in async functionscase from top-levelawait). Nothing ran; there are no logs from your code, only the error itself.
- Name
JS_REFERENCE_ERROR- Type
- category
- Description
A
ReferenceError— some identifieris not defined. The usual suspects: browser/Node APIs that don't exist in the sandbox (fetch,setTimeout,require), a typo'd helper name, orresponseStreamin a non-streaming execution.
- Name
JS_TYPE_ERROR- Type
- category
- Description
A
TypeError— calling something that isn't a function, reading a property ofundefined. Frequently the downstream symptom of a missedJSON.parse(treating an envelope string as an object).
- Name
JAVA_RUNTIME_ERROR- Type
- category
- Description
The failure happened in platform code around the script rather than inside the JavaScript engine — e.g. the execution environment itself failed. Rare; not something your script logic caused.
- Name
UNKNOWN_ERROR- Type
- category
- Description
Anything else the engine threw that doesn't match the patterns above — including your own
throw new Error(...), other JS error types likeRangeError, and errors thrown out of helper methods (say,mathUtils.divideby zero or an interruptedwaitUtils.sleep).
For the three JS_* categories, the categorized message is appended to the execution logs as an entry like [JS_REFERENCE_ERROR] ReferenceError: fetchOrders is not defined, so the logs always name the failure even when you logged nothing yourself.
How errors surface
Three layers work together:
- The wrapper logs the failure point. Every script runs inside a platform-added
try/catchthat callsconsole.errorwith the error's stack trace (or name and message) before re-throwing. Runtime failures therefore always leave a trace in the logs, even if your code has notry/catchat all. - The tool result becomes the error message. For a script tool, a failed execution returns the raw error text (e.g.
TypeError: Cannot read property 'orders' of undefined) as the tool's result — that is what the agent sees, and what it will react to or retry on. - The debugging info carries everything. Each execution's debugging info is the full execution result: the success flag, the final response, the error, and the complete log list (your
console.*output, the wrapper's stack trace, the categorized error line). This is what the test window shows you.
In computed columns there is no tool response to carry the error: a failed script simply writes no value, and the column stays empty.
Errors inside helpers are different from script errors: api.*, llm.invoke, connector.*, and tools.invokeById report their failures as error envelopes in their return values (errorCode/errorMessage fields), not as thrown exceptions. Your script keeps running — which is why every helper page tells you to check the envelope before using the result. An unchecked envelope usually resurfaces two lines later as a JS_TYPE_ERROR.
Forgetting response.setResponse
A script that finishes without calling response.setResponse(...) is not an error — the execution succeeds with an empty result:
- A script tool returns nothing to the agent, which typically leaves it confused about whether the tool worked.
- A computed column treats the empty result as "no value" and writes nothing (occasionally useful — see Computed columns for the leave-unset pattern).
Make setResponse unconditional: every branch, including error branches, should set something, even if it's JSON.stringify({ error: "..." }).
Limits
The sandbox itself imposes no CPU, memory, or wall-clock cap on your script's own code — a plain infinite loop is bounded only by the platform's surrounding task limits, not by an in-engine timer. The practical limits live in the helpers and the platform around the execution:
api.*— 30 seconds to establish a connection, 60 seconds for the full request; a timeout comes back as anIO_ERRORenvelope. See API helper.llm.invoke— 120 seconds per call; a timeout is anINVOCATION_ERRORenvelope. See LLM helper.tools.invokeById— no timeout of its own, but each call synchronously runs the invoked tool's full pipeline, inheriting whatever limits that tool's type carries.waitUtils.sleep— uncapped, but it blocks the whole script and burns the surrounding execution's time budget.- The surrounding execution — script tools run inside an agent task or API call, and the platform's task-level limits bound the total time. A script that outlives its caller gets its threads interrupted, which surfaces as
INTERRUPTED-style helper errors or anUNKNOWN_ERROR.
There are no enforced limits on script length, log volume, or response size beyond those platform task limits.
Debugging workflow
- Log liberally with
console.*. Log inputs on entry, intermediate values after each helper call, and decisions at each branch. Remember plain object logging coerces to an opaque string — logJSON.stringify(obj). Every entry lands timestamped and leveled in the execution logs. - Run it in the test window. The tool builder's test window executes the script with variables you supply and shows the full debugging info — result, error, and the complete log list, including the wrapper's stack trace and the categorized
[JS_*]line for failures. Iterate here, not inside a live agent conversation. - Hand it to Script Copilot. Script Copilot sits in the script editor, sees your script and helpers, and can diagnose a failing run or rewrite the script against the error you paste in.
Two habits prevent most failures in the first place: parse every helper return (envelope first, body second) and check its error fields before touching the data; and validate variables up front, returning a structured error early — see the validation recipe for the pattern.