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 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:
//@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:
//@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:
//@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:
//@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:
//@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:
//@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:
//@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.
//@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:
//@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:
//@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(...).
//@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:
//@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:
//@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. 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 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.
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).
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:
//@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) 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:
//@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:
//@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:
//@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.
Fix: one pane builtin per script. Move the second pane into its own script and add both to the chart.
Broken:
//@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:
//@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:
//@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.
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:
//@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. |
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. |
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() 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 ships:
//@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.