Diagnostics
Every ts2 diagnostic has a code. This page lists them all, with the fix.
Codes are grouped by band: TS2E0xx is syntax, TS2E1xx is meaning,
TS2E2xx is markup, state and refs, TS2E3xx is interfaces and the build’s
declarations. Five codes are warnings — the build succeeds — and are marked
as such. Everything else is a refusal.
Syntax — TS2E0xx
Section titled “Syntax — TS2E0xx”TS2E001 — unsupported construct
Section titled “TS2E001 — unsupported construct”A declaration the language has no form for: an anonymous function, a
destructuring pattern other than a tuple, a function without a body. Give the
function a name — every function is a const bound to an arrow — and take
parameters and tuples apart one name at a time.
TS2E002 — unsupported statement
Section titled “TS2E002 — unsupported statement”A statement the language does not have. Only assignments and calls stand alone
as statements; &&= and ||= are written out as an if and an assignment.
Supported syntax lists what is available.
TS2E003 — unsupported expression
Section titled “TS2E003 — unsupported expression”The widest code: an expression that is well-formed and cannot be built. The message names the shape. The ones you will meet:
===between two strings — test.length,includes,startsWithorendsWithinstead.?.— a read that may miss isValue | null; discharge it with??or=== null..lengthor[i]on a constant array — give the entry a name; take the count from a collection.- A module-level
constwith a type annotation, read inside a function — leave the annotation off. - An object literal as a value — a record belongs in a collection or a constant
array;
ref.props = { … }is the one other place. !on a plain value — only a parameter field (item.weight!) and a component’s geometry through aComponentIdmay be asserted.??or a collection read in awhilecondition — compute it inside the loop.- A shift,
^,~or**— there is no such arithmetic. intArraywithout a literal size — write a literal size.
TS2E010 — exceptions
Section titled “TS2E010 — exceptions”try, catch and throw. There are no exceptions; a read that can fail says
so in its type instead.
TS2E011 — == or !=
Section titled “TS2E011 — == or !=”Use === and !==.
TS2E012 — conditional expression in a while condition
Section titled “TS2E012 — conditional expression in a while condition”? : is fine everywhere else. Compute it into a variable inside the loop body.
TS2E013 — for loop
Section titled “TS2E013 — for loop”Use while, the one loop. for…of and for…in are the same refusal.
TS2E014 — break or continue
Section titled “TS2E014 — break or continue”Neither exists. Restructure the loop’s condition. A break at the end of a
switch case is allowed and ignored — cases never fall through.
Meaning — TS2E1xx
Section titled “Meaning — TS2E1xx”TS2E100 — no type for this expression
Section titled “TS2E100 — no type for this expression”The build could not give an expression one of the language’s types — an empty
array, an Int[], a value with no annotation and nothing to infer from.
Annotate it with a type from Types, or declare a
collection where an array was meant.
TS2E101 — not a type the language has
Section titled “TS2E101 — not a type the language has”A parameter, a prop, a return type or a tuple element is declared with a type
the client cannot hold: a function type, a rest parameter, an array type. Every
parameter and return type is one of the language’s own types. A function
is not a value; pass a Ref to the thing that
should change.
TS2E110 — condition is not a comparison
Section titled “TS2E110 — condition is not a comparison”if (stock) is refused; if (stock > 0) is what you meant. There is no
truthiness. A Boolean value stands alone; anything else is compared.
TS2E111 — ! on something that is not a true/false value
Section titled “TS2E111 — ! on something that is not a true/false value”!count is refused; count === 0 is what you meant. ! works on a Boolean.
TS2E120 — unknown callee
Section titled “TS2E120 — unknown callee”The name being called is not a function the build knows: not imported from a
.ts2 file, or declared inside another function. Declare functions at module
level; an arrow inside a component may be a handler
and nothing else. The same code covers a global called with the wrong shape —
tileOf takes three axes, componentOf two halves in 0..65535, interfaceOf
one module-scope ref — and param given something that is not an item, an NPC
or a world object.
TS2E121 — an event field read outside a handler
Section titled “TS2E121 — an event field read outside a handler”The event is a handler’s parameter and exists nowhere else. In markup, use state or a prop.
TS2E122 — a value that must be a constant is not one
Section titled “TS2E122 — a value that must be a constant is not one”A state declaration’s initial value — sessionState(base() + 1) — or the
default of a function parameter that a call leaves out — (a: Int, b: Int = a * 2). Use a literal, a named const or an enum member, or pass the
argument. A component prop’s default is TS2E209.
TS2E123 — not a game reference
Section titled “TS2E123 — not a game reference”A hook or an accessor was given a plain number, or the wrong kind of reference,
where it needs an entry from a dictionary:
useServerState a game variable, useInventory a container, param and
row.int a parameter. The same code says when a container is used as a
value — index it, or call one of its accessors.
TS2E124 — state was not declared
Section titled “TS2E124 — state was not declared”useState was given a name that is not a state declaration, or a hook that
belongs to a component was called outside one. Declare shared state at the top
level with sessionState or
persistedState, or pass a
literal initial value for state private to the component. An identifier as the
initial value is read as a declaration, so useState(SECONDS) asks for shared
state called SECONDS.
TS2E126 — two declarations share a persisted key
Section titled “TS2E126 — two declarations share a persisted key”Give one of them a different key — and remember that changing a key players already hold orphans the values saved under it.
TS2E128 — two names the build cannot tell apart
Section titled “TS2E128 — two names the build cannot tell apart”Two functions in one file whose names differ only in case — Counter and
counter — are refused. Rename one. The same name in two different
files is fine: names are qualified by their file.
TS2E129 — a ref where a component id is expected
Section titled “TS2E129 — a ref where a component id is expected”A handle from useRef() was passed to a prop or a parameter typed ComponentId
that wants an absolute address, or the other way round. Declare the parameter
ComponentId when a function should take either, and pass event.component
where an address is wanted. See Refs.
TS2E130 — a function that returns nothing returned a value
Section titled “TS2E130 — a function that returns nothing returned a value”A function declared : void has a return x;. Declare the return type, or drop
the value. Several values are returned as a tuple: return [x, y]; with a return
type of [Int, Int].
TS2E132 — arithmetic on event.subId
Section titled “TS2E132 — arithmetic on event.subId”event.subId is an address, not a row number. To learn which row of a .map() fired, name the callback’s
index and use that. Passing subId on whole, or comparing it to -1, is fine.
TS2E133 — private state cannot hold a string
Section titled “TS2E133 — private state cannot hold a string”useState('') is refused: a private cell holds a whole number. Declare the string at module level with sessionState('') or
persistedState('', key) and pass the declaration to useState. See
Interface state.
TS2E135 — two components compared
Section titled “TS2E135 — two components compared”A function compares a parameter it was handed against another component, and
the caller passed event.self. Pass event.component, and event.subId beside it if the position is
part of the question.
TS2E136 — a component polls more than one string cell
Section titled “TS2E136 — a component polls more than one string cell”A component may read one string cell. Split the component, or derive one string from the other.
Markup, state and refs — TS2E2xx
Section titled “Markup, state and refs — TS2E2xx”TS2E200 — unknown element
Section titled “TS2E200 — unknown element”A lowercase tag that is not one of the eight elements, or a const holding
markup used as a tag. Check the spelling and the capitalisation: your own
components start with a capital letter, and a named piece of markup is spliced
in as {banner}, not <banner />.
TS2E201 — unknown prop
Section titled “TS2E201 — unknown prop”The element does not accept that prop. Its reference page lists what it takes; shared props are on Common props.
TS2E202 — a prop cannot fall through to this component’s root
Section titled “TS2E202 — a prop cannot fall through to this component’s root”A prop the component does not declare is applied to its root element, and this one cannot be: the root does not take it (a handler never falls through — bind it on the layer that carries the click), the component has no single root, or you gave one axis of a pair whose partner the root sets as something other than a constant. Declare the prop, or give both axes. See Props.
TS2E203 — unsupported child
Section titled “TS2E203 — unsupported child”Something appeared among an element’s children that cannot be there: a value
that is not markup, a .filter() whose test is not a comparison, a .map()
callback that is not an arrow. Children are elements, fragments, .map() and
.scan() walks, and conditionals.
TS2E204 — bare text between tags
Section titled “TS2E204 — bare text between tags”Text goes in the text prop of a <text>; it is not a component.
TS2E205 — this element cannot have children
Section titled “TS2E205 — this element cannot have children”Only <layer> may contain other elements, and only a component that declares
children can be given any. Wrap the group in a layer.
TS2E206 — this cannot be a handler
Section titled “TS2E206 — this cannot be a handler”A handler is an arrow function or a named .ts2 function, taking the event
whole or nothing. Refused here: an expression that is not a call, a destructured
event parameter, onX={null} on an element being created (null is for a ref
write or one arm of a conditional), and onLoad under a component that reads
state. See Events.
TS2E208 — this cannot be a root
Section titled “TS2E208 — this cannot be a root”Three shapes. A tag whose function does not return Component — give it that
return type. A component that reads state, placed as the root of a conditional
inside a component that rebuilds — wrap the conditional in a <layer>. A root
with no place of its own in the layout, which a rebuild could not tear down —
the same fix. A component that reads state may otherwise be rooted at another
component freely.
TS2E209 — missing required prop
Section titled “TS2E209 — missing required prop”The message names it. A component prop with no default was left out — give it
a value, or give the prop a literal default. A default that is not a literal
counts as none: color = Colors.WHITE is reported here at the call that leaves
color out; write color = 0xffffff. The same code says when a pair is
half-written: x needs y beside it in ref.props, an options record needs
its label, a timer needs a literal everyMs.
TS2E210 — bad geometry value
Section titled “TS2E210 — bad geometry value”x, y, w or h was given something that is not a number, a keyword, a
percentage or an anchor object; an anchor object with two anchors; a size
anchor with a percentage inside it ({ fill: '10%' } — write "10%" for the
proportion or { fill: n } for the inset); or a keyword arriving as a run-time
string. See Layout and geometry.
TS2E211 — flow container child has no size
Section titled “TS2E211 — flow container child has no size”A direction-set layer places each child after the one before, so every child
needs a fixed size on the flow axis, and no child may be a conditional, a loop or
a component. Give the child a number, or drop direction and position the
children yourself.
TS2E212 — a walk over something that is not constant
Section titled “TS2E212 — a walk over something that is not constant”Every field of a mapped constant array’s entries must be a constant — no spread, computed field or row that is not a record. A collection handed in as a parameter cannot be iterated. Iterate a collection by the name it was declared under; read single values from one you were handed.
TS2E213 — a slot given twice
Section titled “TS2E213 — a slot given twice”{children} appears more than once — a component has one slot — or a component
was given two timers, where it carries one.
TS2E214 — bad ref
Section titled “TS2E214 — bad ref”ref must be a plain name declared with useRef() or createRef(), bound to
exactly one element; a <ui> or a static element takes a module-scope
createRef only. draggable, stopTimer, resumePauseButton and the last
argument of a timer or a watch each want something that names a component — a
bound ref, a ComponentId parameter, event.self. As a warning, it says a
module-scope ref inside a .map() row cannot be named from outside the build;
bind it on an element the tree builds once if something outside needs it.
TS2E215 — ref not bound, or read too early
Section titled “TS2E215 — ref not bound, or read too early”A useRef() that no ref={…} ever binds; a ref’s geometry read above the
markup that binds it, when the component does not exist yet; a property that
cannot be set through a ref; interfaceOf on a ref that is not bound on a
<ui> or a static element. Bind the ref, move the read below the binding or
into a handler, and write paired props through ref.props. See
Refs.
TS2E216 — bad options
Section titled “TS2E216 — bad options”options takes a list of labels or option records, written literally, named as
a const, or chosen between two of those with ? :. An empty entry skips a
slot; [] through a ref clears the menu.
TS2E217 — bad hover
Section titled “TS2E217 — bad hover”hover is an object of plain prop overrides for this element. It cannot be
combined with onMouseEnter/onMouseLeave, which bind the same events; its
releaseAfterMs is a literal and cannot share a component with onTimer.
TS2E218 — this would never fire
Section titled “TS2E218 — this would never fire”A subscription that cannot fire: a watch on a kind of thing the hook does not
watch, or with nothing to watch, or a second watch of the same kind on one
component; onVarTransmit and its siblings written as props (a subscription is
a hook — useWatch); a useDelay in a component that rebuilds; a cell read only inside
onUpdated. See
Watching and reacting and
Timers.
TS2E219 — no font
Section titled “TS2E219 — no font”A <text> or <input> with no font draws nothing at all — an invisible,
clickable gap. Add one.
TS2E220 — bad loop shape
Section titled “TS2E220 — bad loop shape”A .map() in a form that cannot be built: as the root of a component that
rebuilds (wrap it in a <layer>), over a component that renders itself, over an
array or a container whose size is not known here. The message names the
missing fact.
TS2E221 — bad interaction flag
Section titled “TS2E221 — bad interaction flag”continueOp, dragTarget, spellTarget and allowRunScript must be literal
true or false; spellTargets a literal list of target kinds;
interactionDepth a literal 0–7; an option’s slot within the ten the menu can
show.
TS2E222 — needs the extended dialect
Section titled “TS2E222 — needs the extended dialect”The project is set to "rift-standard". Either target the Rift client, or use something else. See
dialect.
TS2E223 — bad contentType
Section titled “TS2E223 — bad contentType”contentType takes one of the named surfaces or a plain number, written
directly.
TS2E224 — static is not reachable here
Section titled “TS2E224 — static is not reachable here”A static element sits under something that is built when the interface opens.
The message names the part that kept
its builder — a component that reads state, has a conditional, or holds a ref.
Move the slot above that part.
TS2E225 — a local component reads state
Section titled “TS2E225 — a local component reads state”A component declared inside another function cannot read state. Declare the component at module level.
TS2E226 — markup returned from the wrong place
Section titled “TS2E226 — markup returned from the wrong place”A statement after an early return of markup, or markup returned from
somewhere the tree cannot be planned. See
Conditional rendering.
TS2E227 — bad running total
Section titled “TS2E227 — bad running total”.scan() given the wrong number of by callbacks or starting values, a by
that is not a single-expression arrow, a callback naming a total the walk does
not carry, or a .scan() over a constant array. See scan.
TS2E228 — state written but never read
Section titled “TS2E228 — state written but never read”const [, setSeen] = useState(0) writes a cell nothing reads. Read the
value in the render, or reach for the hook that has the read built in —
useOnResize for a resize, useRerenderOn for an occasion the client
announces. See Watching and reacting.
TS2E229 — a record read beside its test (warning)
Section titled “TS2E229 — a record read beside its test (warning)”row !== null && row.name reads the field beside the test rather than under
it; on a missing row the interface stops drawing.
Put the read under the test — if (row === null) { return null; } and then
read.
TS2E230 — a handed ref bound again
Section titled “TS2E230 — a handed ref bound again”A component bound ref={target} where target is a Ref it was handed. Write through
the ref; bind only your own useRef(). See
Refs.
TS2E231 — an optional Ref
Section titled “TS2E231 — an optional Ref”target?: Ref is refused. Make the prop required, or take a ComponentId
and pass one that always exists.
TS2E232 — spread attributes
Section titled “TS2E232 — spread attributes”{...props} on an element. Write each prop out.
TS2E233 — a caller’s prop overrides the component’s own (warning)
Section titled “TS2E233 — a caller’s prop overrides the component’s own (warning)”<Row color={…} /> where Row sets color on its own root. The caller wins.
Declare the prop on the component when it should decide.
TS2E234 — an option handler on a component that never carries an option
Section titled “TS2E234 — an option handler on a component that never carries an option”An option handler written on useUniverse() never runs. Give the options and their handler to the
same element, in markup.
TS2E235 — a hand-written interface number
Section titled “TS2E235 — a hand-written interface number”componentOf(n, …) where n is a number this build assigned. It moves whenever
an interface is added or renamed. Address the component through the module-scope
ref its interface declares. See componentOf.
TS2E236 — useServerStateOnce outside a render
Section titled “TS2E236 — useServerStateOnce outside a render”Use the hook in a component’s render body. See Read once and derived.
TS2E237 — a subscribing hook inside a read-once body
Section titled “TS2E237 — a subscribing hook inside a read-once body”useState, useWatch, useDerived, useInventory, useOnResize,
useRerenderOn or a nested useServerStateOnce inside the body. Move it out.
TS2E238 — useServerStateOnce in a .map() row
Section titled “TS2E238 — useServerStateOnce in a .map() row”The rows would share one value. Make the row a component, so each instance has its own.
TS2E239 — a read-once capture in a cell with no setter
Section titled “TS2E239 — a read-once capture in a cell with no setter”const [v] = useState(useServerStateOnce(…)) polls a value that cannot change.
Drop the useState and use the value directly; keep the cell only when
something calls its setter.
TS2E251 — a list that rebuilds instead of updating in place (warning)
Section titled “TS2E251 — a list that rebuilds instead of updating in place (warning)”A component that reads state holds a loop whose rows can move — a .filter()
on state, or a loop inside a conditional — so the component rebuilds its rows on every change. Move the
test inside the row. See Rendering lists.
TS2E252 — a listener inside an empty box
Section titled “TS2E252 — a listener inside an empty box”A state read, a timer or a watch inside an element sized 0 on either axis,
with no ref to size it later. It never updates. Give the box
w={1} h={1}, or a ref if a handler sizes it.
TS2E253 — a store into an array you were handed
Section titled “TS2E253 — a store into an array you were handed”ids[0] = 1 where ids is a parameter. Copy it first — const mine = intArray(ids.length); — and write into that.
TS2E254 — .map() over an array of unknown size
Section titled “TS2E254 — .map() over an array of unknown size”.map() over an array that arrived as a parameter. Walk it with while (i < ids.length).
TS2E255 — a region that can never be re-created (warning)
Section titled “TS2E255 — a region that can never be re-created (warning)”A conditional region in a component that updates in place, whose condition is
true for every larger value of a counter that only goes up — build > 0. Put rebuildOn={build} on the region’s
container. See How updates happen.
TS2E256 — rebuildOn with no region
Section titled “TS2E256 — rebuildOn with no region”rebuildOn on an element with no conditional among its children gates nothing.
Put it on the element whose children include the {cond && …} to re-create.
TS2E257 — rebuildOn in a component with no in-place update
Section titled “TS2E257 — rebuildOn in a component with no in-place update”The component rebuilds whole on every change already — usually TS2E251 says
why. Restore the in-place update
first.
TS2E258 — rebuildOn key is not a whole number
Section titled “TS2E258 — rebuildOn key is not a whole number”The key is remembered as a whole number. Keep the list in its string
cell and give the region a companion Int cell whose only job is to be the key.
TS2E259 — rebuildOn on a component invocation
Section titled “TS2E259 — rebuildOn on a component invocation”Put rebuildOn on the element inside the component
that holds the conditional.
Interfaces and declarations — TS2E3xx
Section titled “Interfaces and declarations — TS2E3xx”TS2E300 — static is neither an assertion nor a name
Section titled “TS2E300 — static is neither an assertion nor a name”Write static on its own for an anonymous slot, static ref={x} to name it
with a module-scope createRef, or static="name".
TS2E301 — bad interface root
Section titled “TS2E301 — bad interface root”An interface must be returned from a function taking no parameters; a <ui> inside a handler or a
.map() is not one. Its w, h, x and y must be constants the build can
see — a literal, an enum member, a const or an object field, not arithmetic.
A static layer holds one component and other static layers, is named once,
and no two in one interface share a name. An interface with nothing in it draws
nothing. See <ui>.
TS2E302 — static on a non-layer directly inside <ui>
Section titled “TS2E302 — static on a non-layer directly inside <ui>”Only a <layer> may be static directly inside <ui>. Put the rect
inside the component the slot hosts.
TS2E340 — bad wire identity
Section titled “TS2E340 — bad wire identity”An enum marked @wire names values the server keys on, so every member must
have a written whole-number value in range, no two members or enums may share a
name or a value, and the tag must be spelled exactly. See
Server entry points.
TS2E350 — engine hooks need the extended dialect
Section titled “TS2E350 — engine hooks need the extended dialect”defineEngineHooks names the Rift client’s hooks, which a stock client does not
have. Target rift-extended, or drop the declaration.
TS2E351 — a second engine-hooks declaration
Section titled “TS2E351 — a second engine-hooks declaration”The client reads one configuration per game. Merge the two declarations.
TS2E352 — not an engine hook
Section titled “TS2E352 — not an engine hook”A key in defineEngineHooks that is not one of the hooks or assets. The
message lists the hooks.
TS2E353 — bad asset value
Section titled “TS2E353 — bad asset value”An asset name that is not a literal string, or null on an asset the client
cannot start without. Name the asset, or leave the key out for the default.
TS2E354 — mouse position in an option hook
Section titled “TS2E354 — mouse position in an option hook”e.x and e.y are supplied to the enter, hover and leave hooks only; an option
hook is not told where the mouse is.
TS2E355 — an engine hook that behaves like a component
Section titled “TS2E355 — an engine hook that behaves like a component”Markup, useState, event.self or another hook inside an engine hook. No
component runs it: call a function that addresses components by ref, or set
state a rendered component reads.
TS2E356 — bad engine-hooks shape
Section titled “TS2E356 — bad engine-hooks shape”The declaration is export default defineEngineHooks({ … }) at module scope,
with an object literal and inline arrows. See Engine hooks.
Internal — TS2E999
Section titled “Internal — TS2E999”The compiler hit something it did not expect. Please report it with the source that caused it.
Retired codes
Section titled “Retired codes”Four numbers are spent and will never be reused.
- TS2E125 refused a state write from outside the component that read the state.
- TS2E127 refused a component that owned private state and appeared more than once. Each instance holds its own private state.
- TS2E140 was reserved for a missing type annotation and never reported;
TS2E100covers it. - TS2E207 refused a component with nowhere to attach a timer.
Codes you will not meet
Section titled “Codes you will not meet”- TS2E131 — refused where a value is handed to a low-level client operation.
- TS2E134 — refused where a value is handed to a low-level client operation.
- TS2E250 — refused where a value is handed to a low-level client operation.