Reference

Limits Reference

The sandbox limits every kScript runs under, what each one protects, and when you will hit it.

Every script runs in a sandbox with fixed ceilings. They exist so one script cannot exhaust memory, hang the render, or starve other scripts on the chart. You will never notice most of them. The table below lists each limit, the exact error you get when you cross it, and the kind of script that runs into it.

Limits at a glance

LimitValueWhat it capsWhen you hit itError when exceeded
Source budget20Distinct data-source subscriptions per scriptWide multi-venue aggregation. Weighted types count for more (orderbook = 3)Script exceeds the source budget (21/20) (build); Data source limit exceeded at <line>:<col>: count=21, limit=MAX_SOURCES_PER_SCRIPT, max=20 (run)
MAX_OUTPUT_OBJECTS50000Total plotted/drawn objects across the runPushing thousands of values into a plot in a loopOutput object limit exceeded at <line>:<col> (max 50000)
MAX_DRAWINGS_PER_KIND500Drawings of a single kind (lines, boxes, labels, …)Drawing a line.new / box.new / label.new per bar on long historyDrawing per-kind limit exceeded at <line>:<col>: kind=line, count=501, max=500
Total drawings3000Drawings across all kinds combinedMany drawing kinds at once on long historyper-kind error fires first for any single kind
MAX_TABLE_CELLS10000Cells in a single table.new (rows × cols)A large dashboard or data dump tabletable.new cell limit exceeded at <line>:<col>: rows=101, cols=100, cells=10100, max=10000
MAX_COLLECTION_SIZE100000Elements in one array or mapAccumulating every bar's value into one collection foreverCollection size limit exceeded at <line>:<col> (max 100000)
MAX_STRUCT_CONSTRUCTION_DEPTH64Nesting depth when building a struct/object literalDeeply nested literal built in one expressionStruct construction depth limit exceeded at <line>:<col> (max 64)
MAX_RECURSION_DEPTH200Self-calls deep a user function may goUnbounded or very deep recursionRecursion depth limit exceeded at <line>:<col> (max 200)

A few notes that save debugging time:

  • Source budget is per distinct subscription. Identical source(...) calls dedupe to one. Derived series (htf(), math on an existing source) cost nothing because they fetch no new data. The editor and the runtime enforce the same 20, so a script that runs is also accepted by validation.
  • Drawings have two ceilings. 500 of any single kind, and 3000 across all kinds. The per-kind limit almost always trips first.
  • Most limits are about loops on long history. If you create or push something once per bar without bound, you are the script these protect against. Cap the work, or gate it behind isLastBar.

Examples

These scripts each cross one limit on purpose, so you can see the shape that triggers it.

Output objects, by pushing past 50000 values into a plot:

scripts/probes/limits/max_output_objects_boundary.ks
//@version=2
define(title="Max Output Objects Boundary", position="offchart", axis=true)

timeseries trade = ohlcv(symbol=currentSymbol, exchange=currentExchange)

var values = []
if (isLastBar) {
  for (var n = 0; n < 50001; n += 1) {
    values.push(trade.close + n / 1000)
  }
}

plotLine(value=values, colors=["#dc2626"], width=1, label=["Repeated"], desc=["repeated plot values"])
plotLine(value=trade.close, colors=["#2563eb"], width=2, label=["Close"], desc=["anchor for output object boundary"])

Table cells, with a 101 × 100 table that lands at 10100 cells:

scripts/probes/limits/max_table_cells_boundary.ks
//@version=2
define(title="Max Table Cells Boundary", position="offchart", axis=true)

timeseries trade = ohlcv(symbol=currentSymbol, exchange=currentExchange)

if (isLastBar) {
  table.new("top_right", 101, 100, {bgcolor: "#ffffff"})
}

plotLine(value=trade.close, colors=["#dc2626"], width=2, label=["Close"], desc=["anchor for table cell boundary"])

Recursion depth, with a function that descends 201 levels:

scripts/probes/limits/max_recursion_depth_boundary.ks
//@version=2
define(title="Max Recursion Depth Boundary", position="offchart", axis=true)

func descend(n) {
  if (n <= 0) {
    return 0
  }
  return 1 + descend(n - 1)
}

timeseries trade = ohlcv(symbol=currentSymbol, exchange=currentExchange)

var score = trade.close
if (isLastBar) {
  score = descend(201)
}

plotLine(value=score, colors=["#dc2626"], width=2, label=["Score"], desc=["recursion depth boundary"])