Compilation and runtime errors
1Compilation and runtime errors
All LS codes that the Editor and chart can show: message text, cause and fix, common errors with examples, runtime limits and cases where you get na instead of an error.
Each LootScript compiler and runtime message starts with a code like LS2008. The code is stable across versions and independent of the UI language, so it’s convenient to search by it. This page lists every code the terminal can show, with the message text, the cause and how to fix it.
Messages appear in Russian when the terminal interface is Russian; for any other language they appear in English. First — the most common errors, then runtime errors and a complete table.
Where errors are visible
At compile time. The Editor compiles the script on each typing pause. Findings appear in the “Problems” tab at the bottom of the Editor: code, text and position in the form “Line 6:6”. Double‑clicking a line moves the cursor to the error location. The Editor header shows the result: “ok · 12 ms”, “warnings: 2” or “errors: 1”.
At runtime. A script that compiles cleanly can still stop on a bar: operation budget exhausted, an index went out of bounds, unknown symbol, etc. Then the “Output” tab will show a line like “Script stopped: LS6004: …”, and the same reason appears in the Strategy Tester. Other scripts, panels and the terminal continue to run.
Diagnostics have three levels:
| Level | What it means | What happens on the chart |
|---|---|---|
| Error | The script failed to build | The script is not added to the chart: “Script with errors is not added to the chart — see ‘Problems’.” If the script was already on charts, the previous working version remains |
| Warning | The script built, but the result may be misleading — for example, it will repaint on the live bar | The script runs |
| Notice | The script built, but some setting does nothing | The script runs |
How codes are organised
| Codes | Stage | About |
|---|---|---|
| LS0001–LS0009 | Text reading | Characters, strings, numbers, colors, the #version directive |
| LS1000–LS1012 | Parsing | Brackets, blocks, operator order, script declaration |
| LS2000–LS2030 | Types and names | Unknown names, types, arguments, na |
| LS3000–LS3033 | Program semantics | ta.* in branches, recursion, strategies, libraries, request.security |
| LS4000–LS4009 | Compilation limits | Too many series, outputs, calls |
| LS5000–LS5013 | Repainting and test honesty | Values that look different on history than live |
| LS6000–LS6036 | Execution | Budgets, indices, collections, strings, drawing objects |
| LS0999 | Internal fault | A compiler bug — report it |
Common compile errors
LS1009 — missing script declaration
“No script declaration — start with indicator("Name")”. The first statement in the file must be indicator(...), strategy(...) or library(...). The same error appears if the declaration exists but something is written above it. Comments and the #version 1 directive do not interfere.
// Так нельзя: объявление не первое
len = input.int(14, "Длина")
indicator("RSI")indicator("RSI")
len = input.int(14, "Длина")A second declaration in the same file gives LS1008: a script may have only one.
LS2002 — unknown name
“Unknown name ‘…’ — check spelling or declare it earlier.” Most often a typo or a function without its namespace: indicator functions live in ta, math in math, colors in color.
avg = sma(close, 20)avg = ta.sma(close, 20)The same error occurs if you read a variable before the line where it’s declared. The full list of names is in the Function reference.
LS2008 — comparison with na
“Comparison with na is always false — use na(x)”. Any comparison with na, including == and !=, yields false, so the compiler rejects such expressions.
x = ta.sma(close, 50)
plot(x == na ? close : x)x = ta.sma(close, 50)
plot(na(x) ? close : x)The shorter equivalent is plot(x ?? close).
LS2014 — value changes bar-to-bar where a constant is required
“‘…’ in ‘…’ requires a value at least as strong as input, but a series was given — use input.*(…) or a literal.” A window length for ta.* must be known before the first bar. A length chosen by a per-bar condition is not acceptable.
len = close > open ? 10 : 20
avg = ta.sma(close, len)Compute both options and choose the result, not the length:
fast = ta.sma(close, 10)
slow = ta.sma(close, 20)
avg = close > open ? fast : slowIf the length must be user‑selectable, take it from input.int(...).
LS3004 — ta.* call depends on a variable from a branch
“Argument ‘…’ cannot be computed outside the branch — move the ta.* call to the top level.” ta.* functions remember past bars, so the compiler treats them as computed on every bar even if written inside an if. If the argument is declared only inside the branch, on other bars it simply doesn’t exist.
var float avgBody = na
if close > open {
body = close - open
avgBody := ta.sma(body, 10)
}body = close - open
avgBody = ta.sma(body, 10)LS2017, LS2007, LS1006 — strings, conditions and chained comparisons
These three errors share a cause: LootScript does not coerce types implicitly.
| Code | Message | How to fix |
|---|---|---|
| LS2017 | “Cannot add string and number (… + …) — use str.tostring(…)” | "RSI " + str.tostring(r) instead of "RSI " + r |
| LS2007 | “Condition must be bool, but here is …” | if volume > 0 instead of if volume — numbers are not truthy |
| LS1006 | “Comparisons do not chain — write a < b and b < c” | low < close and close < high instead of low < close < high |
LS2018 and LS2019 — name already taken
“‘…’ already declared in this scope” (LS2018) — you declared the variable a second time with =. To change a value use :=.
level = high
level := low“‘…’ shadows a built‑in name — choose another” (LS2019) — a variable, function or type was named the same as a built‑in series or function: close, time, hour, volume. Rename it: startHour instead of hour. Using namespace names like color or position for variables is allowed.
LS2004 and LS2005 — call arguments
“‘…’ expects … argument(s), but … were given” (LS2004) and “‘…’ has no parameter ‘…’” (LS2005). Check the call against the signature in the Function reference: parameter names must be written exactly as shown.
plot(close, colour = color.red)plot(close, color = color.red)LS3011 and LS5011 — strategies
“strategy.* is available only in strategy(…)” (LS3011): orders and position state exist only for a script declared with strategy(...). Replace indicator("…") with strategy("…").
“Strategy reads ‘…’: book and tape have no history, a backtest would be fictional” (LS5011): a strategy is tested on history, but book.* and tape.* have no history — they are na on past bars.
strategy("Book imbalance")
if book.imbalance(10) > 0.5 {
strategy.entry("L", strategy.long)
}For historical order book data there’s the depth namespace: where the order book is recorded it gives values on each bar.
strategy("Book imbalance")
if depth.imbalance(1.0) > 0.5 {
strategy.entry("L", strategy.long)
}LS5000–LS5006 — repainting warnings
These warnings do not stop the script. They say the line on history will look different from what was seen live:
- LS5001 — a value that exists only on the live bar reaches output (
barstate.isrealtime,barstate.isnew). - LS5000 — a
varipvalue reaches output: it accumulates live‑bar ticks that don’t exist on history. - LS5006 —
book.*ortape.*reach output: the series is empty on history. - LS5003 — higher timeframe value from
request.securitywithout[1]: the unclosed bar will repaint. - LS5002 —
lookahead = trueinrequest.security: the script sees the future on history. - LS5005 —
math.randomortimenowreaches output: two runs give different lines.
If that’s intended — e.g. the indicator is meant only for live trading — the warning can be left.
LS3016 — a setting accepted and does nothing
This is a notice, not an error. It appears in scripts ported from other platforms: the parameter exists for compatibility but has no effect in the terminal. The message’s second part explains why.
| What is written | Explanation in the message |
|---|---|
maxBarsBack in the declaration |
history is loaded column‑wise in full |
format, precision, scale in the declaration |
the terminal controls scale and its format |
timeframe in the declaration |
the script is calculated on the chart’s timeframe; another timeframe gives request.security |
| Line width, style or other output setting set by an expression rather than a constant (plot color and mark may change per bar) | this build reads the setting only as a constant |
color on the fourth and following plot |
the panel draws only the first three series |
location.absolute on plotshape or plotchar |
the mark has no series to stick to |
display that the series doesn’t have |
that series has no such surface |
Runtime errors
A runtime error stops the script on the bar where it occurred. The text appears in the “Output” tab as “Script stopped: LS6004: …”.
LS6004 and LS6005 — script runs too long
“Script exhausted its operation budget on the bar — simplify the loop” (LS6004) and “Script exceeded the time limit for a pass” (LS6005). Each bar has a budget of 2,000,000 operations. Time is limited too: the whole historical pass — 2 seconds, one live‑bar tick — 20 ms. Almost always the cause is a loop that doesn’t terminate or iterates over too much data per bar.
var int n = 0
i = 0
while i < 10 {
n := n + 1
}var int n = 0
i = 0
while i < 10 {
n := n + 1
i := i + 1
}LS6002 and LS5004 — reading the future
A history index x[n] cannot be negative: close[-1] would mean the next bar. If a negative number is written literally, the compiler rejects it immediately (LS5004). If it comes from a calculation, the script stops with “Negative history offset — cannot read the future” (LS6002). An index larger than the amount of loaded bars is not an error — it yields na.
LS6001, LS6007, LS6019 — collections and objects
- LS6001 “Index … out of bounds” —
array.get,array.setormatrix.getpast the size. Checkarray.size(a)before access. - LS6007 “Access to an empty object (na)” — a collection was declared but not created:
var array<float> a = na, thena.push(x). Create it witharray.new<float>(). - LS6019 “Drawing object already deleted or evicted by the ceiling — create it anew”. At most 500 objects of one kind live simultaneously; the oldest is evicted. Raise the ceiling via
maxLinesCount,maxLabelsCountormaxBoxesCountin the declaration or reuse the object viaset_*.
LS6017 and LS6018 — symbol and timeframe in request.security
“Symbol ‘…’ unknown — check the format ‘VENUE:SYMBOL’ or set ignore_invalid_symbol = true” (LS6017) and “Timeframe ‘…’ not parsed — expected ‘5’, ‘60’, ‘4h’, ‘1D’, ‘1W’” (LS6018). With ignore_invalid_symbol = true an unknown symbol yields na instead of stopping.
Execution limits
| What is limited | Limit | Code on exceed |
|---|---|---|
| Operations per bar | 2,000,000 | LS6004 |
| Time for history pass / live tick | 2 s / 20 ms | LS6005 |
| Array elements | 100,000 | LS6003 |
| Pairs in dictionary | 50,000 | LS6003 |
| Matrix elements | 1,000,000 | LS6003 |
| ta.* window length | 100,000 bars | LS6034 |
| Line length | 100,000 chars | LS6014 |
| Table cells | 2000 | LS6029 |
| Heatmap layers | 4 (0…3) | LS6036 |
| Drawing objects of one kind | 500 by default | oldest are evicted |
Distinct instrument+timeframe pairs in request.security |
8 | — |
request.security calls |
32 | LS4009 at compile time |
When there’s no error, but there’s no value
Many situations that would crash in other languages give na in LootScript — a gap on the chart, not a stop:
- arithmetic with
nayieldsna:close - xwith emptyxis empty; x[n]deeper than the start of history —na;- integer division by zero —
na; floating division by zero does not stop the script; book.*,tape.*andscreener.*on historical bars —na;- footprint clusters, delta, open interest and order book history —
naon bars where data is absent. Checkcluster.available,delta.availableand the neighbouring flags; ta.*on the first bars while the window fills —na.
na is never drawn as zero on the chart: the line is interrupted. To substitute a value use nz(x), x ?? fallback or fixnan(x).
All codes
LS0001–LS0009 and LS0999 — text reading
| Code | Message | What to do |
|---|---|---|
| LS0001 | Unexpected character “…” | Remove the character that doesn’t belong to the language |
| LS0002 | String not closed — add a closing quote on the same line | Close the quote on the same line |
| LS0003 | Block comment not closed — add */ | Close /* … */ |
| LS0004 | Unknown escape sequence “…” in string | Allowed: \n, \r, \t, \0, \\, \", \' and \u{…}; double a single \ |
| LS0005 | Invalid numeric literal “…” | Check the number’s notation |
| LS0006 | Number “…” does not fit the type | Reduce the number |
| LS0007 | Invalid color literal “…” — expected #RRGGBB or #RRGGBBAA | Six or eight hex digits |
| LS0008 | Digit separator “_” at start, end or doubled | Write 1_000_000 |
| LS0009 | Invalid version directive — expected #version <number> as the first line | #version 1 on the first line |
| LS0999 | The compiler failed on this file — this is a build defect, report it | Report to support, attach the script |
LS1000–LS1012 — parsing
| Code | Message | What to do |
|---|---|---|
| LS1000 | Unexpected: … | Check the line for extra characters |
| LS1001 | Expected “…”, found … | Most often a missing closing bracket |
| LS1002 | Expected an expression, found … | A value is missing after an operator or comma |
| LS1004 | Expected a name, found … | After var, func, . you need a name |
| LS1005 | Expected a block in braces, found … | Add { … } |
| LS1006 | Comparisons do not chain — write a < b and b < c | Split into two comparisons with and |
| LS1008 | Second script declaration — a script may have only one | Leave a single declaration |
| LS1009 | No script declaration — start with indicator(“Name”) | First line must be indicator, strategy or library |
| LS1010 | Too deep nesting (> 64) — move part into a function | Split the expression or block |
| LS1012 | The body of if / for / while is written in braces: if cond { … } | Add braces |
LS2000–LS2030 — types and names
| Code | Message | What to do |
|---|---|---|
| LS2000 | Type mismatch: expected …, got … | Pass the value of the required type |
| LS2001 | Cannot convert … to … | Convert explicitly: str.tostring, math.round |
| LS2002 | Unknown name “…” — check spelling or declare it earlier | Fix the typo, add a namespace (ta.), declare earlier |
| LS2003 | “…” is not a function, cannot call | Remove parentheses: it’s a series or a constant |
| LS2004 | “…” expects … argument(s), but … were given | Check number of arguments in the reference |
| LS2005 | “…” has no parameter “…” | Fix the parameter name |
| LS2006 | Argument “…” passed twice | Remove the duplicate |
| LS2007 | Condition must be bool, but here is … | Compare explicitly: x > 0, not na(x) |
| LS2008 | Comparison with na is always false — use na(x) | na(x) instead of x == na |
| LS2014 | “…” in “…” requires a value at least as strong as …, but got … — use input.*(…) or a literal | Literal, input.* or constant instead of a series |
| LS2015 | “…” declared constant — cannot assign; declare with var | input.*, loop variables and function parameters cannot be reassigned — copy into your variable |
| LS2017 | Cannot add string and number (… + …) — use str.tostring(…) | str.tostring or str.format |
| LS2018 | “…” already declared in this scope | Use := to change value or rename |
| LS2019 | “…” shadows a built‑in name — choose another | Rename |
| LS2020 | Branches give different types: … and … | Make the branches yield the same type |
| LS2021 | switch without default branch “=> …” — add one | Add => value |
| LS2022 | “…” element type cannot be inferred — specify it: … | array.new<float>() |
| LS2028 | Field “…” of type “…” has no type — write it before the name: float … | float price in a type declaration |
| LS2029 | export “…”: an exported function must have all parameter types and a return type | Specify types |
| LS2030 | “…” in “…” is read before the first bar — needs a literal or named constant | Only constants in the declaration |
LS3000–LS3033 — program semantics
| Code | Message | What to do |
|---|---|---|
| LS3002 | “…” calls itself — recursion is forbidden in LootScript | Replace recursion with a loop |
| LS3003 | Mutual recursion forbidden: … | Break the cycle |
| LS3004 | Argument “…” cannot be computed outside the branch — move the ta.* call to the top level | Compute the argument and call outside if |
| LS3006 | “…” shadows a name from an outer scope (warning) | Rename the inner variable |
| LS3007 | “…” outside a loop | break and continue only inside loops |
| LS3008 | return outside a function | return only in a function body |
| LS3009 | export “…” is allowed only in library(…) | Remove export or make the file a library |
| LS3010 | “…” without a single level (profit/loss/stop/limit/trail) — a no‑op (warning) | Provide stop, take or trailing |
| LS3011 | strategy.* available only in strategy(…) | Declare the script with strategy(...) |
| LS3012 | In a library you cannot draw or declare inputs: … | Move outputs and settings into an indicator |
| LS3013 | “…” inside request.security will not become a settings string — declare the input above and pass its value | Declare input.* above and pass the variable |
| LS3016 | Setting “…” accepted and does nothing: … (notice) | You may leave it or remove it |
| LS3018 | Library “…” not found in the libraries folder — its name is taken from library(“…”, N), not from the filename | Check the name in the library declaration |
| LS3019 | Libraries “…” version … not found; installed: … | Specify an installed version |
| LS3020 | “…” inside request.security: book and tape have no history on another series | Remove book.*, tape.* from the expression |
| LS3025 | Function “…” declared but not called — its body is only tentatively checked (warning) | Call the function or delete it |
| LS3026 | “…” requires a stop to size the position: loss/stop on entry or in strategy.exit | Add a stop to the entry or strategy.exit |
| LS3028 | This build does not support: … | Rewrite without that construct |
| LS3029 | switch branch for “…” repeats — the second is unreachable | Remove the duplicate |
| LS3030 | “…” is a chart series, and the request.security expression is evaluated on another series; pass the value via input.* or compute it inside the expression | Compute inside the expression |
| LS3031 | Libraries import each other in a cycle: … | Break the import cycle |
| LS3032 | In a library “…” only function declarations and imports are allowed — here is … | Remove the rest from the library |
| LS3033 | Library “…” does not compile (…) — open it in the Editor and fix it | Fix the error in the library itself |
LS4000–LS4009 — compilation limits
| Code | Message | What to do |
|---|---|---|
| LS4000 | Too many series: … at the limit 512 — remove extra ta.* or reuse variables | Reduce ta.* calls and history variables |
| LS4001 | Program too large (more than 4096 inlinings) — simplify functions | Reduce calls to user functions |
| LS4002 | Too many plots — the panel draws no more than three series | Outputs — plot, plotshape, plotchar, bgcolor — no more than 64; the first three plot are drawn |
| LS4009 | Too many request.security calls: …, limit 32 | Combine the calls |
LS5000–LS5013 — repainting and tester
| Code | Message | What to do |
|---|---|---|
| LS5000 | varip value reaches output — on history it will differ (warning) | Use var if you need identical history |
| LS5001 | Live‑bar‑only value reaches output — it is absent on history (warning) | Use barstate.isconfirmed |
| LS5002 | lookahead = true reads the future — unreachable on real time (warning) | Remove lookahead = true |
| LS5003 | Higher timeframe without offset — unclosed bar will repaint; add [1] (warning) | request.security(…, close[1]) |
| LS5004 | Negative offset shifts output into the past — the line knows the future on history | Remove negative index x[-n] |
| LS5005 | Non‑deterministic value reaches output — two runs will give different series (warning) | Do not output math.random and timenow |
| LS5006 | Data exists only live — the history series is empty (warning) | For order book history use depth.* |
| LS5011 | Strategy reads ‘…’: book and tape have no history, a backtest would be fictional | Remove book.*, tape.* from the strategy |
| LS5012 | Strategy looks ahead (“…”) — the report is marked as unreachable in real time (warning) | Remove lookahead = true |
| LS5013 | “…” enabled: orders execute on bar close, the report is marked (notice) | Remove processOrdersOnClose if you need a realistic report |
LS6000–LS6036 — execution
| Code | Message | What to do |
|---|---|---|
| LS6001 | Index … out of bounds | Check collection size before access |
| LS6002 | Negative history offset — cannot read the future | History index must be ≥ 0 |
| LS6003 | Collection size limit exceeded (…) — the collection was created on line … | Limit growth: remove old elements |
| LS6004 | Script exhausted its operation budget on the bar — simplify the loop | Check loop exit condition |
| LS6005 | Script exceeded the time limit for the pass | Reduce work per bar |
| LS6007 | Access to an empty object (na) | Create the collection or object before access |
| LS6014 | String longer than the 100000 char ceiling — shorten or build it in parts | Shorten the string |
| LS6015 | Unknown timezone “…” — an IANA identifier is required, e.g. “Europe/Berlin” | Timezone in IANA format |
| LS6017 | Symbol “…” unknown — check format “VENUE:SYMBOL” or set ignore_invalid_symbol = true | Fix the symbol |
| LS6018 | Timeframe “…” not parsed — expected “5”, “60”, “4h”, “1D”, “1W” | Fix the timeframe |
| LS6019 | Drawing object already deleted or evicted by the ceiling — create it anew | Check the object or raise the ceiling |
| LS6020 | str.format: placeholder {…} has no argument | Provide an argument for each {n} |
| LS6021 | str.match: pattern exhausted the step budget — simplify it | Simplify the regex |
| LS6026 | Script-wide collection budget exhausted (… cells) — a collection from line … didn’t fit | Reduce the number or size of collections |
| LS6027 | Invalid argument “…” in … | e.g. negative size in array.new |
| LS6028 | String budget exhausted (…) — reuse strings instead of allocating new ones each bar | Do not build new strings each bar |
| LS6029 | Table has more than 2000 cells — this is the panel ceiling | Reduce the table |
| LS6030 | Table cell outside the grid: … | Column and row numbers must be inside table size |
| LS6031 | Built‑in function “…” declared but not implemented — this is a build defect, report it | Report to support |
| LS6032 | Handle does not point to an object of the required kind — the object was freed or the handle is from another run | Do not keep references to deleted objects |
| LS6033 | Field … outside type declaration “…” — this is a build defect, report it | Report to support |
| LS6034 | Window length … exceeds the 100000 ceiling — such a window won’t fit in memory | Reduce the window length |
| LS6035 | Loop step is zero — counter does not move and the end is unreachable | by in for cannot be zero |
| LS6036 | Heat layer … outside 0…3 | layer from 0 to 3 |
Codes this version does not issue
These numbers are reserved in the language but not emitted by the current terminal version: LS1003, LS1007, LS1011, LS2016, LS3000, LS3001, LS3005, LS4003, LS4004, LS5007, LS6000, LS6006, LS6008, LS6022, LS6023. For example, “declaration not on the first line” is reported as LS1009 now, and division by zero no longer stops the script with LS6000.
Next
https://docs.lootx.trade/en/loot/errors