---
title: "Common Errors"
description: "The messages an Indicator author actually meets, each with its cause and its fix, in the order the build produces them: declaration errors first, then the…"
order: 74
section: "faq"
---

<!-- source: docs/indicators/faq/common-errors.md; generated by packages/cli/scripts/gen-indicator-docs.ts, do not edit -->

# Common Errors

The messages an Indicator author actually meets, each with its cause and its fix, in the order the build produces them: declaration errors first, then the sheet validator, then the compiler, then the contract check, then the named runtime refusals, then the chart's own messages. Every heading is the text the tool prints, so a browser find on the message you got lands on its fix.

If your Indicator ran but drew nothing, the problem is probably not an error at all. Skip to [Ran but blank](#ran-but-blank) at the end.

## Where errors show

- **In the editor**, Run stops at the first failing stage and shows each message as a squiggle under the offending span and as a row in the Problems lane, tagged with its stage (`declarations`, `lint`, `metadata`, `compile`, `validate`). Fix, Run again.
- **On your machine**, `om wrun build` and `om wrun validate` print the same messages and exit non-zero. Declaration and sheet messages name `src/indicator.ts:<line>` or the sheet path (`boxes.0.color`); compiler messages carry the compiler's own `file(line,column)` pointer.

The build is staged, so one problem hides the ones behind it: a declaration error stops the build before the sheet is validated, and a sheet error stops it before the compiler runs. Declaration errors arrive together, one line each, under one header:

```text
scaffold build blocked: code-first declaration errors
  src/indicator.ts:7: param options accept only { required, min, max, description }, not 'step'
  src/indicator.ts:12: option 'top' takes an output handle, not a string literal (bind a handle with a top-level const h = output(...) and pass h)
Declarations are extracted statically (the code never runs at build): string-literal names, literal defaults/options, source.field references, top-level statements in src/indicator.ts only.
```

## `option 'top' takes an output handle, not a string literal`

**Symptom:** the build stops at the `declarations` stage with `option 'top' takes an output handle, not a string literal (bind a handle with a top-level const h = output(...) and pass h)`, pointing at a `box(...)` or `segment(...)` line.

**Cause:** a box or segment coordinate is the value `output(...)` returned, not the output's name. `box("band", { top: "hi", ... })` passes a string where a handle goes.

**Fix:** bind the output to a top-level const and pass the const. The same rule covers `bottom`, `yFrom`, `yTo`, `when`, and an output-valued `from` / `to`:

```typescript
import { box, input, line, none, ohlcv, output, overlay, param } from "./sdk/declare";
import { in_close } from "./gen/inputs";
import { emitRow, out_hi, out_lo, out_sma } from "./gen/outputs";
import { p_period } from "./gen/params";
import { Sma } from "./sdk/ta";

param("period", 20, { min: 1, max: 200 });
input("close", ohlcv.close);
output("sma", line, overlay);
// Bind each coordinate output to a const: the box names the handle, never the string.
const hi = output("hi", none);
const lo = output("lo", none);
box("band", { top: hi, bottom: lo, color: "#38bdf8", opacity: 0.15, borderWidth: 0 });

let sma = new Sma(20);
let value: f64 = NaN;

export function init(): void {
  sma = new Sma(i32(p_period()));
}

export function state(): i32 {
  value = sma.update(in_close());
  return isNaN(value) ? 0 : 1;
}

export function finalize(): void {
  out_sma(value);
  out_hi(value * 1.01);
  out_lo(value * 0.99);
  emitRow();
}

export function reset(): void {
  sma.reset();
  value = NaN;
}
```

## `segment 'ray' option 'yTo' references 'top', which is not an output handle`

**Symptom:** `segment 'ray' option 'yTo' references 'top', which is not an output handle; bind the output first (const top = output("...", ...)) and pass that const`.

**Cause:** the identifier you passed is a variable, but not one bound to an `output(...)` call.

**Fix:** bind it with `const`, `let`, or `var` at the top level (the binding may sit below the shape that uses it). The sheet records the output's name, never the handle.

## `output(...) declarations must be top-level statements`

**Symptom:** `output(...) declarations must be top-level statements, not inside a function, class, or expression` (or the same for `param`, `input`, `box`, `segment`, `render.text`, and the rest).

**Cause:** declarations are read from the text without running it, so one inside `init()`, inside an `if`, or inside a class is invisible to the sheet and refused.

**Fix:** move the declaration to the top level of the file. Declarations are static: they cannot depend on a param or a condition.

## `param options accept only { required, min, max, description }, not 'step'`

**Symptom:** the family's option list, followed by the option you wrote.

**Cause:** every family lists its options in the message. `step` has no counterpart (settings take any value inside `min`..`max`); a kScript (legacy) `label` is `description`; kScript's `constraints` object does not exist.

**Fix:** drop or rename the option. The full option list per family is in [Script definition](../functions/script-definition.md).

## `duplicate output name 'sma'`

**Symptom:** `duplicate output name 'sma'`, `duplicate param name`, `duplicate input name`, `duplicate box name`, or `duplicate render declaration name`.

**Cause:** names are unique per family, and shapes share one namespace with outputs (below).

**Fix:** rename one of them.

## `expected a string literal`

**Symptom:** `expected a string literal`, `expected a numeric literal`, `option 'from' takes an integer bar offset literal or an output handle`, or `option 'panel' takes "overlay" or "lower" (a string literal), not 'price'`.

**Cause:** a name built from a variable, a default computed from an expression, a fractional bar offset, or a panel outside the two names. The extractor reads literals only, because the code never runs at build time.

**Fix:** use literals: `param("period", 20, ...)`, `from: -4`, `panel: "lower"`.

## `code-first declarations must live in src/indicator.ts`

**Symptom:** `scaffold build blocked: src/helpers.ts:3: code-first declarations must live in src/indicator.ts (the build derives wrun/metadata.json from the entry file only)`.

**Cause:** machine workspaces only: a `param`, `input`, or `output` call in a second source file.

**Fix:** keep every declaration in `src/indicator.ts`. Helper files may hold functions and classes, never declarations.

## `code-first declarations need at least one input(...) and one output(...) statement`

**Symptom:** `code-first declarations need at least one input(...) and one output(...) statement (found 0 input(s), 1 output(s))`.

**Cause:** a file with no input has no bar grid to walk, and a file with no output has nothing to compute.

**Fix:** declare the primary input (`input("close", ohlcv.close)` is the usual one) and at least one output, even a `none`.

## `input 'BTC-Close' and 'btc_close' both escape to accessor 'in_btc_close'`

**Symptom:** `wrun/metadata.json: input 'BTC-Close' and 'btc_close' both escape to accessor 'in_btc_close'; rename one so generated accessors stay unambiguous`.

**Cause:** an accessor name is the declared name lowercased with every run of characters outside `[a-z0-9_]` collapsed to `_`. Two names that escape to the same identifier would silently share one accessor.

**Fix:** rename one of the two so the escaped forms differ. Prefer snake_case names in the file and the accessors read as written.

## `raw positional slot literals silently rebind`

**Symptom:** `scaffold build blocked: raw positional slot literals silently rebind when metadata params/inputs/outputs change`, followed by one line per finding: `src/indicator.ts:16: getFloat(0) -> use in_close() from ./gen/inputs in state() or p_period() from ./gen/params in init()`.

**Cause:** `getFloat(0)`, `setOutput(0, ...)`, and the raw host imports bind by position and rebind when a declaration is added above them. The build refuses them in every file under `src/` except `src/sdk` and `src/gen`.

**Fix:** use the accessor the message names. `src/sdk/sdk.ts` stays available for variable-index access only.

## `params.0.default: default above max`

**Symptom:** `params.0.default: default above max`, `params.0.default: default below min`, or `params.0.min: min must be <= max`, at the `metadata` stage.

**Cause:** the derived sheet is validated with the same schema the registry and the runtime use; a default outside its own range fails there.

**Fix:** move the default inside the range, or widen the range.

## `an output cannot color itself with color_by`

**Symptom:** `outputs.0.color_by: an output cannot color itself with color_by`, `outputs.0.color_by: color_by 'regime' does not match a declared output`, or `outputs.0.color_by: color_by needs 'colors' with at least 2 entries`.

**Cause:** `color_by` names a different, declared output (usually a data-only `none`), and `colors` lists at least two entries.

**Fix:** declare the decision output and name it; give the palette two or more entries. `width_by` and `widths` follow the same rules (`width_by needs 'widths' beside it (both or neither)`), and `shape_where` cannot gate its own output (`shape_where 'gate' does not match a declared output` when the gate is missing).

## `color must be a hex, rgb() or hsl() color (the fill takes the opacity)`

**Symptom:** `boxes.0.color: color must be a hex, rgb() or hsl() color (the fill takes the opacity)`.

**Cause:** a named color (`"red"`) cannot carry an alpha channel, and the box fill applies the opacity to its color.

**Fix:** use `"#ef4444"`, `"rgb(239, 68, 68)"`, or `"hsl(0, 84%, 60%)"`. Segment and output colors are free-form strings; only the box fill has this rule.

## `a literal bar offset must be within -500..500 bars of the current bar`

**Symptom:** `segments.0.x_from: a literal bar offset must be within -500..500 bars of the current bar` or `segments.0.x_from: a literal bar offset must be an integer count of bars`.

**Cause:** offsets are integers within 500 bars of the current bar.

**Fix:** stay within the range, or pass an output handle for a longer or data-driven reach: its per-bar value is truncated to the offset, and any offset clamps to the loaded range.

## `boxes must declare at most 16 entries`

**Symptom:** `boxes: boxes must declare at most 16 entries` or `segments: segments must declare at most 16 entries`.

**Cause:** sixteen of each per sheet.

**Fix:** a repeating pattern is one declaration gated per bar, not one declaration per occurrence. The zone tracker draws every zone a side ever has with one box and a `when` gate ([Limits](../reference/limits.md)).

## `box name 'range' is already taken by an output`

**Symptom:** `boxes.0.name: box name 'range' is already taken by an output; box and segment names must be unique across outputs, boxes, segments, renderers, and drawings`.

**Cause:** one namespace for everything drawn.

**Fix:** rename the shape.

## `the primary input (index 0) cannot declare missing: "carry"`

**Symptom:** `inputSources.close.missing: the primary input (index 0) cannot declare missing: "carry": its rows define the request grid, so there is no earlier bar to carry a missing one from; declare "nan" or "zero" to densify the grid instead`.

**Cause:** the first `input(...)` is the grid.

**Fix:** give it no `missing` policy (the usual case), or `"zero"` / `"nan"` to densify a sparse primary; put `"carry"`, `"zero"`, or `"nan"` on the secondary inputs ([Data sources](../core-concepts/data-sources.md)).

## `source 'trades' requires a side (BUY or SELL)`

**Symptom:** `inputSources.buy.side: source 'trades' requires a side (BUY or SELL)` or `inputSources.iv: source 'implied_volatility' requires a tenor (ONE_D, THREE_D, ONE_W, ONE_M, TWO_M, THREE_M, SIX_M, ONE_Y)`.

**Cause:** the per-source knobs: `side` on `trades`, `tenor` on `implied_volatility` and `skew`, `token` on `token_supply`.

**Fix:** add the knob to the input's options: `input("buy", trades.volume, { side: "BUY" })`.

## `input 'profile' declares cellType "array", but the cell channel needs abi_version "wrun-2"`

**Symptom:** `input 'profile' declares cellType "array", but the cell channel needs abi_version "wrun-2" (wrun-1 is frozen and has no cell imports)`, `input 'profile' declares cellType "array" without max_cells; guests preallocate max_cells * 8 bytes and the host refuses bigger blocks, so the cap is required`, or `string_slots needs abi_version "wrun-2" (wrun-1 is frozen and has no string slots vocabulary)`.

**Cause:** hand-written sheets only: a celled input, a string slot, a renderer, or a drawing under the first runtime contract, or a celled input without its cap.

**Fix:** set `abi_version: "wrun-2"` in the sheet and give every celled input a `max_cells`. A code-first file never hits this: declaring any of those constructs derives the second contract automatically ([Script definition](../functions/script-definition.md)).

## `max_bytes must be <= 4096 (the per-slot byte cap)`

**Symptom:** `string_slots.0.max_bytes: max_bytes must be <= 4096 (the per-slot byte cap)`, `renderers.0.size: size must be >= 6`, `renderers.0.cells: table 'stats' declares 2x2 = 4 cells but lists 3`, `renderers.0.text: 'note' does not name a declared string slot`, or `renderers.0.y: 'mid' does not name a declared numeric output`.

**Cause:** text fields name string slots, numeric fields name outputs, and the two never substitute for each other; text sizes are 6..64 pixels; a table lists exactly `rows * cols` cells; a slot holds at most 4096 bytes.

**Fix:** declare the slot or output the renderer names, and keep the numbers inside the caps ([Drawing objects](../functions/drawing-objects.md)).

## `ERROR AS200: Conversion from type 'f64' to 'i32' requires an explicit cast.`

**Symptom:** the `compile` stage fails with the compiler's own pointer:

```text
ERROR AS200: Conversion from type 'f64' to 'i32' requires an explicit cast.
    :
 15 │ export function init(): void { sma = new Sma(p_period()); }
    │                                              ~~~~~~~~~~
    └─ in src/indicator.ts(15,46)
```

**Cause:** the file is AssemblyScript, TypeScript syntax over fixed-width numbers. Params and inputs are `f64`; a class period, a loop bound, an array index, or a counter declared as `let count = 0` (an integer) is `i32`, and the compiler refuses to guess.

**Fix:** write `i32(p_period())`, put `i32(...)` around any float used as an integer, and declare float variables as `f64` (`let value: f64 = 0.0`). Going the other way, an integer written to an output widens with `f64(count)`:

```typescript
import { input, line, lower, ohlcv, output, param } from "./sdk/declare";
import { in_close } from "./gen/inputs";
import { emitRow, out_bars_above } from "./gen/outputs";
import { p_period } from "./gen/params";
import { Sma } from "./sdk/ta";

param("period", 20, { min: 1, max: 200 });
input("close", ohlcv.close);
output("bars_above", line, lower, { description: "Consecutive bars closing above the average" });

let sma = new Sma(20);
let streak: i32 = 0; // an integer counter: declare the type, never let it default from a float
let above: bool = false; // a truth value: compare to get one, never assign a number

export function init(): void {
  // Params are f64; a period is i32, so the cast is explicit.
  sma = new Sma(i32(p_period()));
}

export function state(): i32 {
  const close = in_close();
  const average = sma.update(close);
  if (isNaN(average)) return 0;
  above = close > average;
  streak = above ? streak + 1 : 0;
  return 1;
}

export function finalize(): void {
  // Outputs are f64; an integer goes out through an explicit widening.
  out_bars_above(f64(streak));
  emitRow();
}

export function reset(): void {
  sma.reset();
  streak = 0;
  above = false;
}
```

## `ERROR AS200: Conversion from type 'f64' to 'bool' requires an explicit cast.`

**Symptom:** the compiler points at an assignment of a number to a `bool`.

**Cause:** `ready = value` where `ready` is a `bool` and `value` an `f64`. A number is not a truth value here.

**Fix:** compare: `ready = !isNaN(value)`, `above = close > average`. (`if (value)` on a number does compile, as a nonzero test; write the comparison you mean anyway.)

## `ERROR TS2305: Module 'src/gen/outputs' has no exported member 'out_sma'.`

**Symptom:** `ERROR TS2305: Module 'src/gen/outputs' has no exported member 'out_sma'.` followed by `ERROR TS2304: Cannot find name 'out_sma'.` at every call site.

**Cause:** you renamed or removed a declaration and the generated accessor went with it. The accessors are regenerated from the declarations before every build, so the compiler names every stale import and call.

**Fix:** update the import and the call: `output("average", ...)` is `out_average(...)` from `./gen/outputs`, `param("fast_len", ...)` is `p_fast_len()`, `input("btc_close", ...)` is `in_btc_close()`. A `Cannot find name` on something that was never an accessor is a plain undeclared variable.

## `WRUN export 'finalize' has signature () -> f64; the wrun-1 contract requires finalize() -> void`

**Symptom:** after a successful compile, the `validate` stage refuses the module:

```text
WRUN export 'finalize' has signature () -> f64; the wrun-1 contract requires finalize() -> void. The wrun-1 contract is exactly: init(): void, state(): i32, finalize(): void, reset(): void: NO parameters and no return values except state's i32; params reach init and per-bar inputs reach state through the wrun_arg_* host imports (scaffold builds: the generated p_<param>() / in_<input>() accessors, in metadata order), outputs are written in finalize via wrun_output_f64(i, v) + wrun_result_write(count) (scaffold builds: out_<output>(v) + emitRow()), and all state lives in module-level variables.
```

**Cause:** a redesigned signature (`init(args: Array<f64>)`, a `finalize` that returns the value) compiles under AssemblyScript and fails the static contract check afterwards.

**Fix:** keep the exact four signatures. Values leave through `out_<name>(value)` and `emitRow()`, never as return values; params arrive through `p_<name>()`, never as arguments.

## `wrun_cell_block_too_large`

**Symptom:** an evaluation on your machine (a watch, `om metric get`, `om metric series`) refuses by name: the celled input's bar `has 5 cells; max_cells is 4, so the evaluation is refused (a block is never truncated)`.

**Cause:** `max_cells` is a contract, not a hint: the module preallocates that many tuples, and a bar whose block is larger would overflow it.

**Fix:** raise `max_cells` on the input (`input("profile", volume_profile.cells, { max_cells: 512 })`) and rebuild. Nothing is ever truncated on your behalf.

## `wrun_cells_unavailable`

**Symptom:** at plan time, before anything is fetched: `input 'profile' declares cellType "array" but reads source '...', which serves one scalar per bar, not cell blocks; celled inputs read a celled source class (volume_profile, book)`, or the same code for the `trade_volume_by_size` class.

**Cause:** the input reads a class this data plane does not serve, or a scalar source under a celled declaration.

**Fix:** read `volume_profile` or `book` for celled data; side-split `trades.volume` for the tape's scalar halves ([Data sources](../core-concepts/data-sources.md)).

## `wrun_celled_metric_unsupported`

**Symptom:** `om metric screen` or `om backtest` refuses a package that has a `volume_profile` or `book` input, naming the input (and the composition path in when a scalar package reaches it through a metric source).

**Cause:** the screen and backtest replay paths carry no cell blocks yet.

**Fix:** evaluate celled packages through watches, `om metric get`, `om metric series`, and chart previews; screen and backtest a scalar package.

## `wrun_source_set_generated`

**Symptom:** `om wrun source set` refuses with `wrun/metadata.json: this workspace is code-first: the sheet is generated from src/indicator.ts declarations and every build regenerates it from source, so direct sheet edits are rejected`; the agent's metadata argument refuses the same way as `wrun_metadata_generated`.

**Cause:** a derived sheet is derived state. A direct edit would be clobbered by the next build.

**Fix:** edit the declaration instead (`input("yes_odds", odds.close, { symbol: "0x...", outcome: "YES" })`) and rebuild. To hand-edit the sheet again, remove every declaration from the source, or delete `generated_from` and `source_digest` from the sheet.

## `wrun_preview_displacement_unsupported`

**Symptom:** `om chart indicator preview` refuses an output whose `displacement_bars` is nonzero: declared, not yet honored by chart preview.

**Cause:** the preview wire carries times and values and no displacement field, so a displaced output would render on the bar it was computed on.

**Fix:** preview an undisplaced output. Alerts, `om metric get`, and `om metric series` read the undisplaced value by design; the chart host applies the shift.

## `wrun_render_result_too_large`

**Symptom:** a run refuses because the expanded render selection (every selected text mark, label, table cell, shape, and drawing) passed 2 MiB.

**Cause:** a per-bar text renderer over a long history, or a large table rewritten on every bar.

**Fix:** write the slot only on the bars that need a mark (an unwritten slot draws nothing), or move the readout to `render.label`, which keeps one label. The related string caps refuse the same way, never truncating: 4096 bytes per slot, 64 KiB per row, 8 MiB per run.

## Messages from the chart

- **Indicator compiler unavailable: ...**: the browser could not load the compiler (an offline tab, a blocked worker). Reload; nothing in the file is wrong.
- **The package downloads once the indicator compiles cleanly** and **Publish is available once the Indicator compiles cleanly**: fix the Problems lane first; both buttons unlock on a clean Run.
- **The publish window was blocked. Allow popups for this site and try again.** and **The registry did not answer in time. Try again.**: the publish popup, not your file.
- **Publish the Indicator before adding an alert.**: alerts watch published versions only. Publish, then add the alert on the published row.
- **Indicator alerts are not enabled yet.**: alerts on Indicators from the chart are behind a flag on this site today. Install the package on your machine and put a watch on the metric instead ([Alerts](../functions/alerts.md)).
- **This Indicator reads another market. Alerts evaluate the chart's own market only.** and **This Indicator is pinned to a different interval than the chart.**: a hosted alert refuses `symbol`, `exchange`, and `interval` pins. Alerts on pinned packages run on your machine.
- **This Indicator reads a data source alerts cannot evaluate yet.**: the hosted engine does not fetch that source class. Your machine does.
- **trades volume needs side 'BUY' or 'SELL'**, **source class 'odds' is not served by the browser lane yet**, **funding field 'rate_open' is not served by the browser lane**, **implied_volatility needs tenor 'ONE_W', 'ONE_M', or 'THREE_M'**: the chart serves a subset of the sources catalog ([Execution model](../core-concepts/execution-model.md) lists it). The package is fine on your machine.
- **Volume profile data is unavailable for '...' (the volume_profile source lane answered empty or was declined), so the indicator cannot compute.**: the chart's profile lane returned nothing for this market; switch to a market the venue serves profiles for.

## Ran but blank

An Indicator that builds cleanly and draws nothing has no error to show, only a symptom. The kScript (legacy) engine reports these as runtime diagnostics; an Indicator makes the same states visible through its rows:

| Symptom | What is happening | Fix |
| --- | --- | --- |
| nothing on any bar | `state()` returns `0` on every bar: a class whose period exceeds the loaded history, or a guard that never passes | load more history; probe the guard as a `none` output ([Debugging](debugging.md)) |
| a line is empty on every bar | the output is written `NaN` on every bar, or it is declared `none` | find the first `NaN` input; change the plot kind |
| a line stops partway | a value that went `NaN` mid-history: a division by zero, a `missing: "nan"` secondary with no more observations | guard the division; choose `"zero"` or `"carry"` |
| very few marks | a `shape` gate that rarely fires | usually intentional; lower the threshold to check the gate works |
| empty on the chart, fine on your machine | a `symbol`, `exchange`, or `interval` pin the chart ignores, or a source the chart's market does not serve | read the chart's message; evaluate on your machine |
| fine on closed bars, wrong on the newest | a field `reset()` forgot | reassign every module-level variable in `reset()` |

The workflow that turns a symptom into the line that causes it is in [Debugging](debugging.md).
