---
title: Common Errors
description: The kScript errors you are most likely to hit, what causes each one, and how to fix it.
---

Every entry below is a real engine message. The heading is the exact string you will see in the editor, followed by the cause and the fix. Use your browser find (`Cmd/Ctrl+F`) to jump straight to the error text you got.

If your script ran but drew nothing, the error is probably not a hard error at all. Skip to [Runtime diagnostics](#runtime-diagnostics) for the "ran but blank" cases.

## `zero bars to compute; no source data reached the bar loop`

**Symptom:** the run fails immediately with `zero bars to compute; no source data reached the bar loop (add an ohlcv anchor and ensure data is delivered)`.

**Cause:** nothing fed the bar loop. An offchart script with no source has no timeline to iterate over, so the engine never enters the per-bar computation.

**Fix:** anchor the script to a series with `ohlcv(symbol=currentSymbol, exchange=currentExchange)` (or any `source(...)`). One anchor is enough.

Before, no anchor:

```javascript title="Offchart script with no anchor"
//@version=2
define(title="Zero Bars Without Anchor", position="offchart", axis=true)

plotLine(value=barIndex, colors=["#dc2626"], width=2, label=["Bar index"], desc=["offchart script without source anchor"])
```

After, anchored:

```javascript title="Anchored with ohlcv"
//@version=2
define(title="Zero Bars Fixed With Anchor", position="offchart", axis=true)

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

plotLine(value=trade.close, colors=["#2563eb"], width=2, label=["Close"], desc=["ohlcv anchor reaches the bar loop"])
```

## `Plot function 'line' is missing the 'label' parameter`

**Symptom:** the build fails with `Plot function 'line' is missing the 'label' parameter. Expected 1 label(s) for 1 data series.`

**Cause:** every plotted series needs a non-empty `label`, and labels must be unique across the script. The label powers the editor legend and is the output's identity in the alert picker, so the strict build requires one per series and rejects blanks or duplicates. (`desc` is optional and is not validated.)

**Fix:** add a `label` per series and keep the names unique. The count must match the number of series you plot.

Missing `label`:

```javascript title="Missing label"
//@version=2
define(title="Missing Label Gotcha", position="offchart", axis=true)

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

plotLine(value=trade.close, colors=["#dc2626"], width=2, desc=["line plot without label"])
```

Fixed:

```javascript title="Label added"
//@version=2
define(title="Plot Label Desc Fixed", position="offchart", axis=true)

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

plotLine(value=trade.close, colors=["#2563eb"], width=2, label=["Close"], desc=["line plot with required metadata"])
```

## `Script exceeds the source budget (N/20)`

**Symptom:** the run fails with `Data source limit exceeded at <line>:<col>: count=X, limit=MAX_SOURCES_PER_SCRIPT, max=10`.

**Cause:** a script has **10 weighted source slots**, and you declared more than fit. Heavy source types can cost more than one slot, so a handful of them reaches the budget fast. Identical `source(...)` calls dedupe to one, so this is about *distinct* subscriptions, not lines of code.

**Fix:** stay within the 10 weighted source slots, counting weighted types by their weight. Derived series (`htf()`, math on an existing source) are free; they fetch nothing. Identical calls dedupe to one slot.

## `Indexing is only allowed on timeseries and arrays`

**Symptom:** compile and build fail with `Indexing is only allowed on timeseries and arrays at <line>:<col>`.

**Cause:** you used history indexing (`x[1]`) on a `var`. A `var` is a single per-bar scalar with no history, so there is nothing to index back into. Only `timeseries` and arrays carry indexable history.

**Fix:** hold any value you want to look back on in a `timeseries`, then index it.

Broken, indexing a `var`:

```javascript title="Indexing a var"
//@version=2
define(title="Var History Index Gotcha", position="offchart", axis=true)

timeseries trade = ohlcv(symbol=currentSymbol, exchange=currentExchange)
var closeNow = trade.close
var previousClose = closeNow[1]

plotLine(value=previousClose, colors=["#dc2626"], width=2, label=["Previous"], desc=["var history indexing error"])
```

Fixed, indexing a `timeseries`:

```javascript title="Indexing a timeseries"
//@version=2
define(title="Timeseries History Index Fixed", position="offchart", axis=true)

timeseries trade = ohlcv(symbol=currentSymbol, exchange=currentExchange)
timeseries closeSeries = trade.close
var previousClose = barIndex > 0 ? closeSeries[1] : closeSeries

plotLine(value=closeSeries - previousClose, colors=["#2563eb"], width=2, label=["Delta"], desc=["history indexing on timeseries"])
```

Array indexing is always fine, including nested cells:

```javascript title="Nested array indexing"
//@version=2
define(title="Array Celled Index Fixed", position="offchart", axis=true)

timeseries trade = ohlcv(symbol=currentSymbol, exchange=currentExchange)
var vp = [[trade.low, trade.close, trade.high], [trade.open, trade.close, trade.volume]]
var i = 0
var score = vp[0][i + 1] + barIndex

plotLine(value=score, colors=["#16a34a"], width=2, label=["Array cell"], desc=["nested indexing on an array-shaped value"])
```

## Negative indexes: `close[-1]` is not array tail access

**Symptom:** a script that used `close[-1]` no longer reads the next bar.

**Cause:** negative timeseries subscripts return `na`. The old behavior was a lookahead bug: `close[-1]` read a future bar. Arrays still intentionally support negative tail indexing, so `arr[-1]` reads the last array element.

**Fix:** for timeseries, use only non-negative history offsets: `close[0]` for the current bar, `close[1]` for one bar ago. For arrays, keep using negative indexes when you want tail access.

Negative indexing on a series returns `na`, while negative indexing on an array reads from the tail; both are well defined on every bar.

```javascript title="Negative indexes on a series and an array"
//@version=2
define(title="Negative Index Series Array", position="offchart", axis=true)

timeseries trade = ohlcv(symbol=currentSymbol, exchange=currentExchange)
timeseries closeSeries = trade.close
var values = [trade.open, trade.high, trade.low, trade.close]

var seriesCheck = isna(closeSeries[-1]) ? closeSeries : na
var arrayCheck = values[-1] == trade.close ? trade.close : na

plotLine(value=seriesCheck, colors=["#2563eb"], width=2, label=["Series negative"], desc=["negative timeseries history indexes return na"])
plotLine(value=arrayCheck, colors=["#16a34a"], width=2, label=["Array negative"], desc=["negative array indexes wrap from the tail"])
```

## `Undefined identifier '<name>'`

**Symptom:** the build rejects the source call with `source() arg references script var(s) <name>; use a literal, an env identifier (currentSymbol/currentCoin), an input, or an inline env expression`, and the run fails with `Undefined identifier '<name>'`.

**Cause:** you passed a script variable as a `source()` argument. Source arguments are resolved before your script's variables exist, so they cannot reference a `var`.

**Fix:** pass a literal, an environment identifier (`currentSymbol`, `currentExchange`, `currentCoin`), an `input(...)`, or an inline environment expression directly in the call.

Broken, `var` in a source argument:

```javascript title="Script variable in a source argument"
//@version=2
define(title="Source Arg Script Var Gotcha", position="offchart", axis=true)

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

plotLine(value=trade.close, colors=["#dc2626"], width=2, label=["Close"], desc=["script variable used in source argument"])
```

Fixed, environment identifiers inline:

```javascript title="Environment identifiers in a source argument"
//@version=2
define(title="Source Arg Literal Fixed", position="offchart", axis=true)

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

plotLine(value=trade.close, colors=["#2563eb"], width=2, label=["Close"], desc=["source argument uses currentSymbol and currentExchange"])
```

## `first line must be //@version=2 or //@version=3`

**Symptom:** the build fails with `first line must be //@version=2 or //@version=3 (no marker routes to deprecated legacy v1)`, or with `//@version=1 routes to the deprecated legacy engine; use //@version=2 or //@version=3`.

**Cause:** the version header is missing, is `//@version=1`, or is not on the first line (a comment line above it does not count). Without a valid marker the script routes to the deprecated legacy v1 engine, where none of the v3 features exist. `//@version=2` and `//@version=3` are both valid and select the same modern engine; `//@version=3` is the canonical form. Numbers above 3 (for example `//@version=4`) are not valid markers either: the engine logs `Version 4 not supported, using v2` and coerces to modern, but do not rely on that.

**Fix:** make `//@version=3` the literal first line of the script, before `define(...)`.

```javascript title="Version header on line one"
//@version=3
define(title="Version Header Fixed", position="offchart", axis=true)

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

plotLine(value=trade.close, colors=["#2563eb"], width=2, label=["Close"], desc=["script with version header"])
```

## `Unknown math method: avg`

**Symptom:** the run fails with `Unknown math method: avg`.

**Cause:** there is no `math.avg`. It is easy to assume it exists, but it is not part of the `math` namespace.

**Fix:** average the values yourself with `(a + b) / 2`.

Broken:

```javascript title="math.avg does not exist"
//@version=2
define(title="Math Avg Gotcha", position="offchart", axis=true)

timeseries trade = ohlcv(symbol=currentSymbol, exchange=currentExchange)
var average = math.avg(trade.open, trade.close)

plotLine(value=average, colors=["#dc2626"], width=2, label=["Average"], desc=["unknown math.avg method"])
```

Fixed:

```javascript title="Manual average"
//@version=2
define(title="Math Average Fixed", position="offchart", axis=true)

timeseries trade = ohlcv(symbol=currentSymbol, exchange=currentExchange)
var average = (trade.open + trade.close) / 2

plotLine(value=average, colors=["#2563eb"], width=2, label=["Average"], desc=["manual average without math.avg"])
```

## `Got 'bool'` (use `boolean`)

**Symptom:** an `input(...)` type fails with `Got 'bool'`.

**Cause:** the input type is spelled `boolean`. There is no `bool` alias.

**Fix:** use `"boolean"` as the input type name.

## Blank plot from a source member typo

**Symptom:** the script compiles and runs, but the plot is empty. The diagnostics show `COMPUTE_ALL_NAN` and `RENDER_NO_DRAWABLE`.

**Cause:** you read a source member that does not exist (for example `funding.close` when the field is `funding.value`). Member names are not checked at compile time, so a typo silently yields `na` on every bar and the plot has nothing to draw.

**Fix:** check the member list for that source type in [Data Sources](../core-concepts/data-sources.md). For `funding_rate`, the member is `value`, not `close`.

## Blank or flat anchored VWAP

**Symptom:** an anchored VWAP plots nothing, or a perfectly flat line. The diagnostics show `COMPUTE_ALL_NAN` or a flat-line warning.

**Cause:** the loaded window never crosses the anchor boundary. A monthly anchor needs a window long enough to contain a month start; if every loaded bar falls inside the same period, the anchor never resets and the series stays `na`. This is about loaded history, not about whether you stored the result in `var`.

**Fix:** load more bars, or choose a shorter anchor (`day`, `week`) that the window actually crosses. See [Special Indicators](../functions/special-indicators.md#anchored-vwap-data-requirement) for the history each anchor needs.

## `strategy methods require a strategy() declaration`

**Symptom:** the build fails on any `strategy.entry` / `strategy.exit` / getter call.

**Cause:** the script declares `define(...)` (or nothing) but calls `strategy.*` methods. A script is either an indicator or a strategy, never both.

**Fix:** change the first declaration to `strategy(title="...", ...)`. In the editor the first-line keyword is clickable and converts the draft for you. See [Writing Strategies](../strategies/writing-strategies.md).

## `strategy.exit order '<id>' must set at least one of profit, limit, loss, stop, or a trailing pair`

**Symptom:** the run completes but the exit never arms; the diagnostics panel shows `STRATEGY_EXIT_NO_LEGS` and the order counts as rejected.

**Cause:** `strategy.exit(id)` was called without any protective leg.

**Fix:** give the exit at least one of `profit`/`loss` (ticks), `limit`/`stop` (absolute prices), or `trailPoints` (+ `trailOffset`).

## `strategy.exit profit/loss tick legs and limit/stop absolute legs must be finite and greater than 0`

**Symptom:** trades open but their brackets never fire; the diagnostics panel shows `STRATEGY_EXIT_INVALID_LEG` and the exit orders count as rejected.

**Cause:** a leg resolved to `NaN`, zero, or a negative number. The classic shape is deriving the level from price on early bars where the source has no data yet, e.g. `stop=lastClose * 0.97` while `lastClose` is still `NaN` before the pair's history begins.

**Fix:** only place the exit when the level is real, e.g. gate it behind the position: `if (strategy.positionSize() > 0) { strategy.exit("X", stop=lastClose * 0.97) }` (the position can only exist once bars are real), or check the value before using it.

## `Daily backtest limit reached. The counter resets at midnight UTC.`

**Symptom:** the Backtest action fails immediately with this message.

**Cause:** your tier's backtest allowance for the day is spent (free 20 runs/day, plus 200; see [Backtest grants](../reference/limits.md#backtest-grants)).

**Fix:** wait for the UTC reset. Normal chart runs of the same script are unaffected; only explicit backtest runs count.

## `Script must start with a define(title, position, axis) or strategy(title) statement`

**Symptom:** the build fails with `Script must start with a define(title, position, axis) or strategy(title) statement at 2:1`, anchored at the script's first statement.

**Cause:** every script opens with exactly one header declaration, `define(...)` for an indicator or `strategy(...)` for a strategy. Any other first statement (a `timeseries`, a `var`, a plot call) trips this rule before anything else is analyzed. It is also what you get when you call a foreign-pane builtin (`plotMatrix` / `plotCurve` / `plotTiles`) in a script with no header at all: this general rule fires first, so you never reach the pane-specific message in the next section.

**Fix:** make `define(title=..., position=..., axis=...)` (or `strategy(title=...)`) the first statement after the version header.

Broken, foreign-pane builtin with no header:

```javascript title="No define at all"
//@version=3
timeseries trade = ohlcv(symbol=currentSymbol, exchange=currentExchange)

if (isLastBar) {
  plotMatrix(data = [[0, 0, trade.close]], rowLabels = ["last"], colLabels = ["close"], format = "number")
}
```

## `plotMatrix requires define(title, "offchart", ...): foreign-domain panes own a dedicated offchart pane`

**Symptom:** the build fails with `plotMatrix requires define(title, "offchart", ...): foreign-domain panes own a dedicated offchart pane at 7:3`, anchored at the foreign-pane builtin call. The message names whichever pane builtin you called.

**Cause:** foreign-domain panes ([plotMatrix / plotCurve / plotTiles](../functions/foreign-panes.md)) exist only in indicator scripts with an offchart `define`. This variant fires when the script has no `define` for the pane rule to inspect, which in practice means a `strategy(...)` script: the strategy header satisfies the general first-statement rule above, then the pane builtin is rejected here. Like every pane-placement error in this family, it is a compile-time analyzer error: the strict editor build and the compiler report the identical text, and the script never runs.

**Fix:** foreign panes are indicator-only. Move the pane into its own script headed by an offchart `define`.

Broken, pane builtin inside a strategy:

```javascript title="plotMatrix inside a strategy script"
//@version=3
strategy(title="Pane In Strategy", position="onchart", axis=true, initialCapital=10000, qtyType="fixed", qtyValue=1, pyramiding=1);

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

if (isLastBar) {
  plotMatrix(data = [[0, 0, trade.close]], rowLabels = ["last"], colLabels = ["close"], format = "number")
}
```

Fixed, offchart indicator:

```javascript title="Foreign pane under an offchart define"
//@version=3
define(title="Pane Placement Fixed", position="offchart", axis=true)

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

if (isLastBar) {
  plotMatrix(data = [[0, 0, trade.close], [0, 1, trade.open]], rowLabels = ["last"], colLabels = ["close", "open"], format = "number")
}
```

## `plotCurve requires define(position="offchart"): foreign-domain panes ignore the shared time axis (got "onchart")`

**Symptom:** the build fails with `plotCurve requires define(position="offchart"): foreign-domain panes ignore the shared time axis (got "onchart") at 2:1`, anchored at the `define` statement rather than at the builtin call. The `got "..."` clause echoes the `position` your `define` declared, so a typo comes back verbatim: `position="offchar"` produces `... (got "offchar")`. Omitting `position` entirely fails earlier, with `define(...) missing required argument 'position'`.

**Cause:** a foreign-domain pane ignores the shared time axis, so it cannot be drawn onchart over the candles. Any declared `position` other than `"offchart"` rejects the script at compile time.

**Fix:** declare `position="offchart"` in the `define`.

Broken:

```javascript title="plotCurve under an onchart define"
//@version=3
define(title="Wrong Pane", position="onchart", axis=true)

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

if (isLastBar) {
  plotCurve(data = [[0, 1], [1, 2], [2, 4]], series = [{ label: "poc" }])
}
```

## `Only one foreign-domain pane output per script`

**Symptom:** the build fails with `Only one foreign-domain pane output per script: plotTiles conflicts with plotMatrix (line 7) at 8:3`, anchored at the second foreign-pane builtin and naming the line of the first.

**Cause:** a script owns at most one foreign-domain pane, so a second pane builtin rejects at compile time. See [Structural rules](../functions/foreign-panes.md#structural-rules).

**Fix:** one pane builtin per script. Move the second pane into its own script and add both to the chart.

Broken:

```javascript title="Two pane builtins in one script"
//@version=3
define(title="Two Panes", position="offchart", axis=true)

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

if (isLastBar) {
  plotMatrix(data = [[0, 0, trade.close]], rowLabels = ["last"], colLabels = ["close"], format = "number")
  plotTiles(tiles = [{ label: "Last", format: "number" }], data = [[0, trade.close]])
}
```

## `plotLine cannot be combined with plotMatrix: a foreign-domain pane owns the script's whole output`

**Symptom:** the build fails with `plotLine cannot be combined with plotMatrix: a foreign-domain pane owns the script's whole output at 6:1`, anchored at the normal plot call. It fires even when that plot is strict-clean with `label` and `desc`; the combination itself is the error.

**Cause:** a foreign-domain pane owns the script's whole output. There is no shared time axis in its pane to draw a time-series plot against, so `plotLine` and friends cannot ride along in the same script.

**Fix:** remove the normal plot, or move it into a separate script.

Broken:

```javascript title="plotLine next to a foreign pane"
//@version=3
define(title="Combined", position="offchart", axis=true)

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

plotLine(value=trade.close, colors=["#2563eb"], width=2, label=["Close"], desc=["strict-clean line plot"])

if (isLastBar) {
  plotMatrix(data = [[0, 0, trade.close]], rowLabels = ["last"], colLabels = ["close"], format = "number")
}
```

## `Type mismatch for 'plotTiles.texts': expected an array of { tile, text } objects, got number`

**Symptom:** the script builds cleanly, then the run fails with `Type mismatch for 'plotTiles.texts': expected an array of { tile, text } objects, got number at 7:3`.

Every `plotTiles.texts` rejection in this family is a **runtime** error: the strict build passes and the failure surfaces when the script executes. The console reports it as `[kScript] worker run failed (no main-thread fallback): SyntaxError: <message>`, and the `at <line>:<col>` site is the `plotTiles(` call itself, not the `texts` argument.

**Cause:** `texts` must be an array of `{ tile, text }` objects. Any other shape is rejected with the runtime type name of what you passed (here `number`, from `texts = 42`).

**Fix:** pass `texts = [{ tile: <tileIndex>, text: <string> }, ...]`. A working call, for reference across this family; tile 1 gets a computed string, and tile 2's entry is `na`, which skips the entry before any validation so the tile keeps its static `text` fallback:

```javascript title="Computed tile text with an na fallback"
//@version=3
define(title="Tiles Texts Fixed", position="offchart", axis=true)

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

if (isLastBar) {
  plotTiles(
    tiles = [
      { label: "Last", format: "currency" },
      { label: "Regime", format: "text", text: "AWAITING" },
      { label: "Note", format: "text", text: "STATIC FALLBACK" }
    ],
    data = [[0, trade.close]],
    texts = [
      { tile: 1, text: trade.close >= trade.open ? "UP" : "DOWN" },
      { tile: 2, text: na }
    ]
  )
}
```

That run emits exactly two data rows, the numeric row for tile 0 and `[1, "UP"]` for tile 1; tile 2 emits no row and renders its static `"STATIC FALLBACK"` text.

## `plotTiles.texts[0] targets tile 0 ("Last") which is not format "text"`

**Symptom:** the run fails with `plotTiles.texts[0] targets tile 0 ("Last") which is not format "text" at 7:3`, quoting the label of the tile you targeted.

**Cause:** `texts` entries may only target tiles declared with `format: "text"`. Here `texts = [{ tile: 0, text: "UP" }]` pointed at a `"currency"` tile.

**Fix:** target a `format: "text"` tile, or change the target tile's format.

## `plotTiles.texts[0].text must be a string`

**Symptom:** the run fails with `plotTiles.texts[0].text must be a string at 7:3`.

**Cause:** a `texts` entry carried a non-`na`, non-string `text` (here `texts = [{ tile: 1, text: 42 }]`). `na` is the one non-string that does not error: an `na` text skips the whole entry before index and format validation, and the tile falls back to its static `text` (see the working call above).

**Fix:** compute a string, or pass `na` to deliberately keep the static fallback.

## `plotTiles.texts[0].text exceeds 32 chars`

**Symptom:** the run fails with `plotTiles.texts[0].text exceeds 32 chars at 7:3`.

**Cause:** computed tile text is capped at 32 characters. The message above came from a 45-character string: `texts = [{ tile: 1, text: "THIS COMPUTED REGIME TEXT IS WAY OVER THE CAP" }]`.

**Fix:** keep computed tile text to 32 characters or fewer. Tiles are for compact states ("UP", "RISK-OFF"), not sentences.

## `plotTiles.texts has duplicate tileIndex 1`

**Symptom:** the run fails with `plotTiles.texts has duplicate tileIndex 1 at 7:3`.

**Cause:** two `texts` entries targeted the same tile (`{ tile: 1, text: "UP" }` and `{ tile: 1, text: "DOWN" }`).

**Fix:** at most one `texts` entry per tile.

## `plotTiles.data[1] targets text tile 1 ("Regime"): text tiles take a string value, not a number`

**Symptom:** the run fails with `plotTiles.data[1] targets text tile 1 ("Regime"): text tiles take a string value, not a number at 7:3`.

**Cause:** a numeric `data` row (`data = [[0, trade.close], [1, 1]]`) pointed at a tile declared `format: "text"`. Text tiles take strings, and strings reach them through the `texts` kwarg or the static `text` field, never through numeric `data` rows.

**Fix:** point numeric rows at numeric tiles; give text tiles their strings via [the `texts` kwarg](../functions/foreign-panes.md#computed-text-the-texts-kwarg).

## `Mixed array types not supported`

**Symptom:** the build fails with `Mixed array types not supported at 12:31`, pointing at an array literal that mixes numbers and strings.

**Cause:** kScript array literals are homogeneous. The classic way to hit this is trying to author a string inside a `plotTiles` `data` row, `data = [[0, trade.close], [1, "FROM DATA"]]`: the mixed `[tileIndex, string]` tuple cannot be written as a literal.

**Fix:** keep `data` numeric. Strings for text tiles go through the `texts` kwarg, which exists precisely because this literal cannot be authored.

Broken:

```javascript title="String inside a plotTiles data row"
//@version=3
define(title="Tiles Texts Data Collision", position="offchart", axis=true)

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

if (isLastBar) {
  plotTiles(
    tiles = [
      { label: "Last", format: "currency" },
      { label: "Regime", format: "text", text: "AWAITING" }
    ],
    data = [[0, trade.close], [1, "FROM DATA"]],
    texts = [{ tile: 1, text: "UP" }]
  )
}
```

## Runtime diagnostics

Some scripts compile and run, then draw nothing useful. The engine catches these and emits a **runtime diagnostic** instead of leaving you with a blank panel. Each one has a `code`, a stage (`compute` or `render`), and a severity. A clean run reports none of them.

`error`-severity diagnostics mean nothing drew. `warning`-severity diagnostics let the run through but flag a likely problem.

| Code | Stage / severity | Message | Cause and fix |
| --- | --- | --- | --- |
| `COMPUTE_ZERO_BARS` | compute / error | `runtime had zero bars to compute; no source data reached the bar loop` | No source delivered data to the bar loop. Add an `ohlcv` anchor and make sure the run has data. See [the section above](#zero-bars-to-compute-no-source-data-reached-the-bar-loop). |
| `RENDER_NO_PLOT` | render / error | `runtime produced no plot outputs, so there is nothing to draw` | The script computed but called no `plot*`. Add at least one plot or drawing. Also the deliberate outcome when a data-quality guard leaves a foreign pane with nothing to plot; see [the fallback case below](#a-blank-foreign-pane-after-a-requestbars-fallback). |
| `COMPUTE_ALL_NAN` | compute / error | `plot "<id>" is NaN for every computed bar` | A plotted series is `na` on every bar: an indicator that never warmed up, a wrong source member, or an anchor that never crossed. Check warmup length, member names, and anchor windows. |
| `RENDER_NO_DRAWABLE` | render / error | `plot "<id>" has no finite values to draw` | The render-side companion to `COMPUTE_ALL_NAN`: the plot has no finite value anywhere. |
| `COMPUTE_TRAILING_NAN` | compute / warning | `plot "<id>" becomes NaN after bar <n>` | The series was finite, then went `na` partway through (often a divide-by-zero or a lookback that outran the data). The line stops drawing at that bar. |
| `RENDER_SPARSE_OUTPUT` | render / warning | `plot "<id>" has only <n> drawable value(s) across <m> bars` | Very few finite points relative to the bar count, usually a condition that rarely fires or a marker on isolated bars. Often intentional; flagged so a near-blank plot is not mistaken for a bug. |

### A blank foreign pane after a requestBars fallback

**Symptom:** a foreign-pane script runs green but the pane is empty. The run reports success on every check; the only signal is a `RENDER_NO_PLOT` diagnostic, `runtime produced no plot outputs, so there is nothing to draw`, with the suggested fix `add at least one plot call that emits a drawable series`.

**Cause:** [`requestBars()`](../core-concepts/multi-source.md#requestbars-raw-bars-for-drawing) asks for a native timeframe, but a venue that does not serve it falls back to chart-interval rows. Multi-day chart rows would silently mislabel daily buckets (a bar's close can land in the calendar bucket after its open), so a well-written pane script guards on row spacing and plots nothing rather than plotting something wrong. Zero plot outputs is the deliberate outcome here, not a failure.

The guard, distilled. It is the same guard the [monthly-returns example](../functions/foreign-panes.md#full-example-monthly-returns-interval-independent) ships:

```javascript title="Spacing guard on requestBars rows"
//@version=3
define(title="Guarded Daily Pane", position="offchart", axis=true)

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

if (isLastBar) {
  var rows = requestBars(currentSymbol, "1d", { bars: 400, anchor: "latest" })

  // Real dailies step ~86,400,000 ms even around weekend gaps; multi-day
  // fallback rows step more, and would mislabel daily buckets.
  if (rows.length > 1) {
    var minStep = na
    for (var i = 1; i < rows.length; i = i + 1) {
      var step = rows[i][0] - rows[i - 1][0]
      if (isnum(minStep) == false || step < minStep) {
        minStep = step
      }
    }
    if (isnum(minStep) && minStep > 129600000) {
      rows = []
    }
  }

  if (rows.length > 0) {
    plotMatrix(
      data = [[0, 0, rows.length], [0, 1, rows[rows.length - 1][0] - rows[0][0]]],
      rowLabels = ["daily"],
      colLabels = ["rows", "span ms"],
      format = "number"
    )
  }
}
```

Observed on the three venue shapes, 300 rows delivered in each run:

| Venue serves | Min consecutive step | Outcome |
| --- | --- | --- |
| Native `1d` rows | 86,400,000 ms | Pane renders; span cell `25,833,600,000` ms (299 x 86,400,000) |
| Intraday fallback (hourly rows) | 3,600,000 ms | Guard passes (3,600,000 <= 129,600,000); pane renders over shallower history; span cell `1,076,400,000` ms |
| Multi-day fallback (3-day rows) | 259,200,000 ms | Guard trips (259,200,000 > 129,600,000), `rows = []`, `plotMatrix` never runs, zero outputs, `RENDER_NO_PLOT` |

Without the guard, the same 3-day feed renders 300 "daily" rows spanning `77,500,800,000` ms, roughly three times the native-daily span for the same row count: plausible-looking numbers computed from rows that are not dailies. That silent wrongness is what the guard trades for an empty pane. The full monthly-returns script behaves the same way end to end: on a venue with native dailies it renders the year-by-month grid with its AVG / MED / WIN% summary rows, and on a multi-day fallback venue it renders an empty pane with exactly this diagnostic.

**Fix:** two honest ways out. Run the script on a venue that serves the native timeframe you asked for, or decide that intraday depth is acceptable and let intraday fallback rows through; they bucket correctly by timestamp, just over shallower history, which is why the guard's threshold (`129600000` ms, one and a half days) already admits them.
