Errors

When something goes wrong, the editor tells you what category of error it is and where to look. This page covers all of them.

Look-ahead protection

The most important property of the API first: it is built so that an indicator cannot quietly read the future.

on_bar runs once per bar in time order and only ever sees the current bar and the bars before it. ctx.close[1] looks one bar back; there is no index for a future bar. That is what makes a backtest built on your indicator honest — it reflects what a trader would actually have seen at the time.

On top of that, every Apply and every chart computation runs a look-ahead scan over your source. Patterns that peek ahead — shifting a series backwards, reversed slices, negative iloc, backward-filling, taking a whole-series aggregate like mean() of all bars — are rejected with a Look-ahead violation error that names the line. Rewrite the logic bar by bar with the built-in helpers and it goes away.

Indicators that pass the scan are marked backtest-safe, which is what the Backtest-safe only filter in the library shows.

Error categories

Error What triggers it What to do
Validation error A parameter is the wrong type, outside its min/max, or not in choices; or a source param's value is malformed. Read the message — it names the parameter and the bound. Fix the declared default or the value in the panel.
Security check A forbidden import, builtin, or attribute trick (see below). Remove it. There is no override — the sandbox protects shared infrastructure. Do that work outside the indicator and pass the result in via a parameter.
Look-ahead violation The scanner found code that reads bars after the current one. Rewrite it bar by bar. The message names the line and the pattern.
Code error / traceback Your code raised an exception, no Indicator subclass was found, or the class defines a compute() method (not allowed — use init/on_bar). Read the traceback; it's filtered to your code (see below).
Timeout The run took too long: 20 s on a live chart, 30 s for Validate, 300 s in a backtest. Use the built-in helpers instead of hand-rolled per-bar loops, and shrink large window sizes. Helpers are vectorised and rarely time out at sensible parameter values.
Memory limit The run exceeded 1.5 GB. Usually an unbounded list growing every bar — cap what you retain on self.
Rate limit Too many chart computations in a short burst. Wait a moment and retry.
Data error No price data exists for the requested symbol or range. Pick a different range or symbol.
Auth error Your session expired. Sign in again.

Security check in detail

The sandbox blocks anything that does I/O, spawns processes or threads, or loads code dynamically. Representative blocked modules: os, sys, subprocess, socket, ssl, urllib, requests, importlib, shutil, pathlib, tempfile, threading, asyncio, multiprocessing, pickle, inspect, gc. Blocked builtins: exec, eval, compile, open, input, breakpoint, globals, locals, vars, __import__.

Two rules catch otherwise ordinary-looking code:

  • Reflection attributes such as __class__, __dict__, __globals__, __builtins__, __subclasses__ and __mro__ are blocked wherever they appear.
  • Dynamic attribute names — getattr(obj, name) / setattr / hasattr where name is not a string literal — are blocked. Spell the attribute out.

These are checked statically at save time, so you find out immediately — not mid-render. Everything an indicator legitimately needs (numpy, pandas, scipy, the stdlib math/stats modules, and the built-in ctx helpers) is available; see Scripting basics.

Reading tracebacks

When your code raises an exception, the traceback shown in the editor is filtered. Only frames from <user_indicator> (your source) are kept; internal frames are stripped, and the line numbers map to your editor. Read it like any Python traceback — start at the bottom.

If after filtering there are zero frames, the error pane shows a <system> sentinel. That means the failure was in our code, not yours — please report it with the source that triggered it.

Stuck? Here's what to do.

When you're staring at red and aren't sure what to try next, work down this list:

  • Read the lint marker in the editor. The red squiggle has a message that points at the exact line. Most errors are obvious once you read what they actually say.
  • Run Validate (Ctrl/Cmd + Enter). It runs your init then on_bar over the sample dataset and surfaces a real traceback if your code throws. The editor's inline check catches typos; Validate catches problems that only show up when the code actually runs.
  • Check helper warm-up. A finite-window helper returns None until it has enough bars. The most common silent bug is using .value in arithmetic without an if value is not None: guard — that raises a TypeError partway through the run.
  • Plot the value you're unsure about. print() output is not shown in the editor. Instead, draw the number with ctx.plot.series("debug", value=x, pane="sub") or pin it with ctx.plot.text(...) and Apply — you'll see exactly what your code computed on every bar.
  • Ask the AI. The AI button in the editor can read the traceback together with your code and usually spots the cause.

If you've done all of that and you're still stuck, start a fresh draft from a built-in that does something similar and diff it against yours — see Working with the editor.

Documentation