Errors and results
When a run fails, Waiso does not hand you a wall of text. The Output tab shows an error block: the error’s type, its message, and the stack as a list of frames — your own lines first, the library internals folded away. Click a frame to land on that line; press Fix with AI to send the error, the code and the inputs to the assistant. When a run succeeds with a list of records, the same tab shows it as a table you can search, sort and download.
- Read the error block: type, message and frames
- Jump from a frame to the failing line
- Ask the assistant to fix the error with the context attached
- Search, sort and export a list result as CSV
What a failed run tells you
Every failed run carries a structured error, not just a line of text. It says what kind of failure it was, its type (ZeroDivisionError, RangeError, timeout…), the message, and — for an error your code raised — the frames: file, line and function for each step of the stack, each marked as your code or not. The same record is what the Runs page, an API caller and a flow step see.
| Kind | What happened | Frames |
|---|---|---|
| Exception | Your code raised or threw an error (a syntax error included) | Yes, for Python and TypeScript/JavaScript |
| Input | An input was refused before the run (invalid_parameter) — see Script inputs | No |
| Platform | A timeout, a cancel, a lost worker, a package that would not install | No |
Read an error and jump to it
Run this script with its default (an empty list). Python divides by zero; the TypeScript version throws its own RangeError.
def average(values: list[float]) -> float:
return sum(values) / len(values)
def main(values: list[float] = []):
return {"average": average(values)}- Read the headline
The block opens with the type over the message:
ZeroDivisionError— division by zero. A platform failure shows its code instead, such astimeout; the log’s last line says it in words (The script exceeded its timeout). - Follow your frames
Below it, each of your frames reads line 2 in average, with that line of code beside it — the deepest call last. Click one: the editor puts the cursor on that line and centres it. Frames from the runtime, the SDK or installed packages fold into Show 3 internal frames; a very deep stack keeps its first and last frames and folds the middle.
- Check the logs
The Logs tab still has everything the script printed before it failed, with a search box and a level filter (All levels, Info, Warning, Error, System).
Fix with AI
At the foot of an exception’s block, Fix with AI opens the editor’s AI chat tab with the message Fix the error this run raised. already typed and a chip ZeroDivisionError · code and inputs attached. Nothing is sent until you press Send — add a sentence of your own if you know what the fix should be, or remove the attachment with its ×. The assistant answers with a proposed change you review as code before it touches your draft; see The editor’s AI chat.
The button is offered for errors your code raised. A platform failure (a timeout, a lost worker) has nothing in the code to fix, so it has none; a package that would not install offers Declare package or Change version instead, which open the Dependencies panel with the package filled in. Copy error copies the whole error as text.
def average(values: list[float]) -> float | None:
if not values:
return None
return sum(values) / len(values)
def main(values: list[float] = []):
return {"average": average(values)}Results as a table
A successful run shows what main returned as formatted JSON. When the result is a list of records — an array of objects with up to 50 different keys between them — it opens as a table instead: one column per key, a Search rows box that matches any cell, a row count (2 of 3 rows while searching), headers that sort on click (ascending, descending, back to the original order) and a CSV button that downloads the rows. The icons beside it switch between table and JSON, copy the JSON, download it as a .json file, or expand the result to fill the screen.
def main(limit: int = 3):
products = [
{"sku": "A-100", "name": "Stapler", "sold": 42, "price": 12.5},
{"sku": "B-220", "name": "Pen", "sold": 130, "price": 2.0},
{"sku": "C-310", "name": "Backpack", "sold": 17, "price": 89.9},
{"sku": "D-404", "name": "Ruler", "sold": 64, "price": 3.4},
]
products.sort(key=lambda p: p["sold"], reverse=True)
return products[:limit]| # |
|---|
What each language reports
- Python — the exception’s class and message, frames from the traceback; a syntax error points at its line and column.
sys.exit()fails asSystemExit. - TypeScript and JavaScript — the error’s
nameandmessageand frames from its stack. Throw anError(not a bare string) to keep the frames. - C and C++ — a non-zero exit code or a crash fails the run with the last line the program wrote to
stderrand the exit code or signal (terminated by signal 11 (SIGSEGV)); there are no frames. A compile error stops the run before it starts, with gcc’s message in the error text.
Platform codes you may meet
| Code | Meaning |
|---|---|
timeout | The run went over its timeout (5 minutes unless changed in Script settings) |
cancelled | Someone pressed Stop |
worker_lost | The worker running it disappeared mid-run |
worker_unavailable | No worker could start it (also a C/C++ compile error, or a process killed before answering) |
dependency_* | A Python package could not be installed — see Dependencies |
Limits
- A stack keeps at most 60 frames (the first 20 and the last 40); the traceback text keeps its last 64 KB.
- Clickable frames and Fix with AI are in the editor. On the Runs page the same error is shown read-only.
- Search, sort and CSV work on a top-level list of records; a single object can be switched to a key/value table but opens as JSON.