Errors and Diagnostics
Diagnostics as you read them
An error has one of two shapes, and the shape says when it happened: a static listing before anything runs, or a run-time text that catch sees.
The static listing (before anything runs)
int x = 5;
x = "hello"; // typed to typed: the load fails, nothing runs
print("never printed");jaiscript: static check failed:
<script>:2:3: error: cannot assign 'string' to 'x' declared 'int' (declared at 1:5)
x = "hello"; // typed to typed: the load fails, nothing runs
^The shape is FILE:LINE:COL: error: MESSAGE, then the source line, then a caret.
| Part | Rule |
|---|---|
FILE | The unit's resolved path (the root file, an import, an include); <script> for source text handed to execute(). |
| Message | Lowercase, except the few that reuse a run-time text and keep its capital (Cannot access private member 'x' of class 'C', Cannot access private constructor of class 'C'). One about a declared name ends with (declared at LINE:COL). |
| A failed load | Hands the host one jai::static_check_error whose what() is the whole listing (sorted by location, deduplicated, capped at 100). Nothing has executed. |
| Warnings | Shadowing, a copying cast: the same shape with warning:; they never stop the load. The runner lists them under --warnings; a host reads them from last_check_diagnostics(). |
| A syntax error | The same shape under parse failed:, thrown as jai::parse_error. The parser lists every syntax error in source order (it resumes after each); no check runs until the source parses. |
Checked at load, by site
Each row is an error whenever the types involved are declared or inferred (auto holds the type it inferred). Chapter: Strict Checking.
| Site | What is checked | Example message |
|---|---|---|
| Names | every identifier is declared where the use can see it; top-level code sees only what is declared above it (top-level order), a function body also what is declared below; a qualified name exists in its namespace | use of undeclared identifier 'totl'; no member 'sqroot' in namespace 'math' |
| Declared types | every spelled type names a class, enum or builtin type, inside container and handle types too | unknown type 'vec2'; did you mean 'math::vec2' (using math::vec2;)? |
| Redeclaration | one declaration of a variable per scope, the top level of a file included | redeclaration of 'a' in the same scope |
| Stores | a declaration, =, op=, a field, a static, a return and a pushed or literal element against the slot's type, by the conversion table | cannot assign 'string' to field 'hp' declared 'int' |
| Float into int | refused at every slot where both types are visible, a store through an int& and a coroutine's yield included | cannot initialize 't' declared 'int' with 'float': cast<int>(x) truncates toward zero, math::ifloor(x) rounds down |
| A vec into an ivec | refused at every slot where both types are visible (a variable, a parameter, a return, a field, an element, a store through a reference); an ivec widens into a vec | cannot initialize 'cell' declared 'math::ivec2' with 'math::vec2': cast<math::ivec2>(v) truncates each component toward zero |
| Text or a container into bool | a declared bool slot refuses a string, an array and a map; conditions keep truthiness | cannot initialize 'b' declared 'bool' with 'string': write !x.empty() or an explicit comparison |
| Class values | never null, in a variable, parameter, field, static, return or element; a class cannot hold itself by value, directly, through another class or through a base | cannot initialize 'h' declared 'Hero' with 'null': a 'Hero' is never null; declare 'shared_ptr<Hero>' (or var) for a slot that can be empty |
| Enums | an enum is a type: an int or another enum's value does not enter its slots, ++ and += do not apply, and a value must be one the enum declares (Enums) | cannot initialize 'd' declared 'Dir' with 'int': cast<Dir>(n) converts an int that is one of its values |
| Calls | a function, method or constructor the checker knows, another file's and a namespace's (ns::f) included: argument count (defaults counted) and each argument; with overloads, an error only when none fits | 'fib' expects 1 argument(s), got 2; argument 1 of 'game::add': cannot pass 'string' to 'n' declared 'int' |
| Builtin calls | a builtin method's and a math:: function's arguments against the shapes the API reference states: a number, text, an array, a map, a function, a vector or color. A parameter that takes several shapes is not checked | argument 1 of 'math::sqrt': cannot pass 'string' where a number is expected; argument 1 of 'substr' on 'string': cannot pass 'string' where a number is expected |
| Host calls | a function the host registered and a method it bound: argument count and each argument against the C++ signature (a number for a number, text for a std::string, an array, a map). A script class below the bound class that declares the method leaves the call to run time | 'host_scale' expects 2 argument(s), got 1; no overload of 'Probe.set' accepts these argument types |
| Default arguments | a default cannot read a parameter | default value of 'b' names parameter 'a': a default cannot read a parameter |
print / format | the literal format text's placeholders against the arguments that follow it, none included | the format text of 'print' has 2 placeholder(s) for 1 argument(s); the format text of 'print' has 1 placeholder(s) for 0 argument(s) |
| Namespace variables | a store to and a read of ns::x against its declared type | cannot assign 'string' to 'game::score' declared 'int' |
| Stores through a reference | a T& of a scalar, text, container or math type stores what a T slot takes | cannot assign 'string' to 'a' declared 'int&' |
instanceof | the right side names a class; an operand whose type can hold no object | 'instanceof' tests a class, and 'Dir' is an enum: compare the value with one of its values (x == Dir::value); 'instanceof Unit' is always false: the operand is 'int', never an object of a class |
| Writes through a const loop variable | a writing method call or a field store through a const range-for variable or structured binding over value elements of a known class | cannot call 'hit' on 'm': 'm' is const and 'hit' writes its receiver (line 3) |
| Reference bindings | a T& binds only a T lvalue; a literal reaches a T& parameter only when the function never writes through it (Value Semantics) | 'bump' writes through parameter 'n' (line 1); pass a variable |
| Members | a member the receiver's declared class lacks, a member only a subclass declares included; a method an array, map or string lacks; any member of an int, float, bool or char but same_as | no member 'range' in class 'Unit'; no member 'pusj' on 'array<int>' (did you mean 'push'?) |
| Methods and statics | a method read without a call; a static read through an instance or through Class.name | 'n' is a static member of class 'C': name it C::n |
| Field initializers | an initializer reads only the fields declared above it; a lambda it creates may name any field | field initializer of 'spd' reads 'slices' before its initialization: ... |
| Map subscripts | the key against the map's declared scalar key type (a char and its one-character string are one key) | Cannot use 'string' as a key of type 'int' |
| Casts | T names a type; the operand is judged when the cast runs | unknown type in cast: 'Nope' |
Common messages
| Message | Meaning |
|---|---|
cannot assign 'string' to 'x' declared 'int' (declared at 8:3) | a typed store the table rejects |
cannot initialize 'x' declared 'int' with 'float': cast<int>(x) truncates toward zero, math::ifloor(x) rounds down | A float into an int slot, at every site: a declaration; a store (cannot assign 'float' to 'x' declared 'int', to field 'n'); a compound assignment; an argument; a return; an element (cannot push into 'array<int>': expected 'int', got 'float', cannot insert into); a store through an int&; a typed range-for variable; an int coroutine's yield (cannot yield 'float' from 'g' declared to return 'int'); an int/float conditional (its type is float). |
default value of 'b' names parameter 'a': a default cannot read a parameter | a default argument that names a parameter of its own list |
cannot assign to member 'hp' of a map; store the key as ["hp"] | a dot store on a map the checker knows |
cannot initialize 'a' declared 'Vec2' with 'null': a 'Vec2' is never null; declare 'shared_ptr<Vec2>' (or var) for a slot that can be empty | null into a class value slot (a declaration, a store, an argument, a return, a field initializer) |
class 'Node' holds itself by value (Node.next): a class value is never null; make one link a 'shared_ptr<Node>' (an array or a map of them starts empty) | a class reaches itself through value fields or a base (A.b -> B.c -> C.a, Derived : Base -> Base.d); an error in a warn region too |
cannot initialize 'd' declared 'Dir' with 'int': cast<Dir>(n) converts an int that is one of its values | an int or another enum's value into an enum slot; d += 1 and d++ are cannot apply '++' to 'Dir' |
cannot initialize 'b' declared 'bool' with 'string': write !x.empty() or an explicit comparison | a string, an array or a map into a declared bool slot |
fill() cannot store into 'array<int>': expected 'int', got 'string' | a fill, resize, assign or array<T>(n, v) value the element type rejects |
resize() size must be an int, got 'string' | a size that is not an int (assign, reserve alike) |
cannot initialize 'c' declared 'char' with 'int' | the same at a declaration (char c = 65) |
cannot initialize 's' declared 'string' with 'int': stringify it with to_string(x) or `${x}` | a string slot takes text or a char, never a number, bool, null or container (a store, a parameter, a return and a field carry the same hint) |
cannot initialize 'c' declared 'char' with 'string': a char takes a string of exactly one character | A string literal of another length than one into a char slot: a declaration, a store, an argument, a return, a field, an element or a map key, each with its own lead and the run-time text's reason. |
cannot apply '+' to 'string' and 'int': stringify it with to_string(x) or `${x}` | A number, bool or null beside a string's +, in either order: the run-time error's text, at load. A template string's splices are text; a char joins as its character. |
the format text of 'format' must be a string literal: vformat(fmt, ...) formats a run-time text, and + or a template string joins text | format(s, ...) over a string that is not a literal: only a literal is a format string (print and format) |
unexpected '#' before 'xs': JaiScript has no length operator; write xs.size() | a parse error: Lua's #xs. Any other # is unexpected '#': it begins only #include, #import and a first-line #! shebang |
advance() cannot store the yielded 'string' into 'int' | a coroutine handle's advance(v) whose declared yield type v's type does not take (advance() writes the yielded value through its argument; pass a variable for a literal) |
'sleep' cannot be called in a parallel body | A sleep in a parallel_for body, or in a lambda handed to parallel_transform or as parallel_reduce's step (its combine is ordinary code). In a destructor or in hot_reload_migrate it is a warning that it blocks there. |
nesting too deep for the host thread's stack | the source nests deeper than the parser or the checker can walk on the host thread's stack (run-time twin) |
the result of 'f' is discarded, but 'f' is [[nodiscard]]: use it, or keep it in the placeholder (auto _ = f(...);) | an expression statement drops the result of a [[nodiscard]] function, method or host callable ([[nodiscard]]) |
the result of 'string::to_upper' is discarded: it returns a new value and leaves its argument unchanged, so the statement does nothing; keep the result, or call the in-place method (name.to_upper(...)) | the same for a library copy of an in-place method (string::, array::, map::filter) |
'e.hp' writes to 'e', a copy of the element that is never read afterwards, so the write is lost; iterate with 'auto& e' to change the element | a write into a by-value copy nothing reads again (Lost writes); a parameter's text ends take 'T& p' to change the caller's value, a local's declare 'T& x' to change the original |
coroutine 'g' cannot take 'n' by reference: it runs on after the call returns, past the argument's lifetime; take it by value (int n), or pass a shared_ptr to share an object | a parse error: a coroutine parameter declared T&, const T&, auto& or var& (Coroutines) |
[[nodiscard]] applies to a function or a method | a parse error: the attribute leads a variable, a field, a constructor or a destructor; [[nodiscard]] function 'f' returns void: there is no result to keep for a void function |
redeclaration of 'x' in the same scope (declared at 3:9) | A block, a parameter list or one unit's top level declares a variable name twice. Not second declarations: a later run of the unit, another unit, an extern's definition, an inline declaration, a local or structured binding named _ (the C++26 placeholder). A class's fields, or a field and a method: redeclaration of member 'x' in class 'C' (declared at 2:15). |
'_' is declared more than once in this scope, so it names none of them: give the variable you use its own name | a use of _ where its scope declares the placeholder more than once (auto _ = f(); auto _ = g();); a single _ is an ordinary variable ([[nodiscard]]) |
use of undeclared identifier 'G' | no declaration anywhere in the program; extern if the host provides it |
'math' is a namespace, not a value; name one of its members (math::name) | A namespace's name read as a value (int spd = math;): a host one (math, time, reflect, crypto, binary, string) or a script's. In a [[warn_mode]] region the run time raises Undefined variable 'math'. A class's name read as a value is its constructor (random among them): a typed slot other than a function refuses it with cannot initialize 'spd' declared 'int' with 'class random'. |
field initializer of 'spd' reads 'slices' before its initialization: fields initialize in declaration order; declare 'slices' above 'spd' | a field initializer reads its own field or one the class declares below it, bare or through this; a lambda it creates may name any field (Classes) |
Unary minus requires numeric operand · Unary plus requires numeric operand · Bitwise NOT requires integer operand | The run-time texts (Arithmetic), at load, where no row takes the operand's declared type: a bool, a string, a container, a function, null, a float or a math value under ~, a class whose chain declares no such operator. |
unknown type 'Nope' | A declared type (a variable, field, parameter, return, range-for variable, element, key, value or pointee) names no class, enum or type of the program or the host. A bare math name adds ; did you mean 'math::vec2' (using math::vec2;)?. A warning in a unit with a dynamic-path include. |
no member 'sqroot' in namespace 'math' | A qualified name no unit of the program, earlier run or host registration declares in that namespace (a script's namespace math { ... } adds to math::). For a::b::c the longest prefix naming a namespace is the one named. Declare a member the host registers after load with extern (extern function math::scale;, extern). A warning in a unit with a dynamic-path include. |
int has no member 'abs' · Cannot use 'string' as a key of type 'int' | The run-time texts (builtin type has no member, key type), at load: a member other than same_as of a known int, float, bool or char; a subscript key a map's declared scalar key type refuses. |
cannot initialize 'd' declared 'int' with 'enum Direction': an enum's name is its value table, a map<string,int>; name one of its values (Direction::north) | an enum's name read as a value is its value table: a slot that cannot hold a map<string,int> refuses it (var, auto and map<string,int> take it) |
no member 'zz' in class 'Vec2' | the receiver's class (a value's, a handle's or a reference's) is fully known and has no such field or method: cast<T&> if it lives on a subtype |
cannot compare 'int' with 'string' using '==': the types have no comparison (it is always false); convert one operand | A comparison between two declared types no conversion or comparison operator relates. != is always true; an ordering raises Invalid operands for < operator. A class operand: the text names the class that declares no comparison operator. |
cannot apply '*' to 'string' and 'int' (it raises 'Invalid operands for * operator'); convert one operand | An arithmetic or bitwise operator between two declared types no builtin row takes and no operator of the left operand's class answers. Builtin rows: numbers and chars take + - * / %; a string on either side takes +; ints and chars take & | ^ << >>. A class on the left is named (class 'Gator' declares no operator '*'); a compound names its spelling (cannot apply '*=' to 'int' and 'string'). |
cannot apply '++' to 'bool' | ++ or -- on a declared type that is not a number or a char (a script class has no ++). On a slot declared char the int it steps to is the store error cannot assign 'int' to 'c' declared 'char' |
cannot compare 'int' with null using '==': 'int' is never null (it is always false); compare with a value of its type, or declare the slot var to hold null | null against a typed slot that never holds it: int, float, char, bool, string, utf8_string, an array, a map, a math value, an enum or a class value. Not checked: a call's container result, which can be null. Handles, functions and var compare with null. |
'fib' expects 1 argument(s), got 2 / argument 1 of 'fib': cannot pass 'string' to 'n' declared 'int' | Arity and per-argument checks against a callee the checker sees. A name with overloads in other units or earlier executes is checked against all of them (no overload of 'g' matches this call (2 argument(s))). |
'g' is already defined (declared at 1:10) | a second function with the same signature; overloads must differ in parameter types or count |
'G' is used before its declaration (line 2): top-level code runs in order, so declare it above the use or make the use after it · 'f' is called before its declaration (line 2): ... | Top-level code reads a variable, a class or a function this file declares only further down; a function or class body may use them anywhere. A name another unit or an earlier run defined is there. A namespace's top level alike: a bare name it declares further down reaches an enclosing namespace's member or the global first, and is this error when neither exists. |
reference variable 'r' must be initialized: a reference names a variable from its declaration on | a T& declaration without an initializer; nothing loads |
argument 1 of 'bump': cannot bind 'float' to 'x' declared 'int&' | a T& parameter binds a variable of exactly T (a class T or a subclass); a const T& parameter also takes what a T parameter takes, bound as a temporary |
method 'speak' redefines 'Cat::speak': mark it 'override' | a method that redefines a base class method says override (and method 'm' marked as override but no base class method found when there is none) |
cast<C>(h) copies the instance - use cast<C&>(h) to alias it or cast<shared_ptr<C>>(h) to share it | a warning: the cast is legal, but probably not what you meant |
map keys of class 'C' compare by identity: declare operator<=> (or operator<) or hash() to key its values by value | a warning, from the checker at the subscript and once per class from the run time: a value of C keys a map by identity |
No bound 'int * Meter': bind operator "*" as (int, Meter) on Meter, or convert the operand | a warning: a number on the left of a bound class's arithmetic operator the class binds no reflected form of |
'f' declared to return 'int' may reach the end of its body without returning | a primitive, string, enum, class value or reference return is required on every path |
'heal' is a method of Player; call it with p.heal() or bind it with reflect::bind(p, "heal") | a method read without a call |
to_upper changes name in place and returns nothing; string::to_upper(name) returns an uppercased copy | A call uses the result of an in-place builtin on a known array, map, string or handle (run-time twin). |
'bump' writes through parameter 'x' (line 1); pass a variable | a literal or temporary passed to a T& parameter the body writes through |
argument 3 of 'parallel_reduce': step 'one' must take two parameters (acc, x) | a declared step or combine of another arity (argument 4 ... combine ... (a, b) alike) |
cannot bind a reference to a conditional of 'int' and 'float': its arms must have one type | a T& argument, declaration, default, return or cast<T&> over a conditional whose arms name storage of two types (one arm type binds the chosen variable) |
cannot bind non-lvalue default to reference parameter 'p' | a T& parameter's default that can never name storage (int& p = 5); a const T& one binds a temporary, and a coroutine binds a copy |
cannot assign to a call result: 'get' returns by value · cannot assign to x++: it yields the old value · cannot assign to a literal | an assignment target that names no storage (the runtime texts below, lowercased) |
regex: missing ')' to close the group at position 1 | a regex(...) whose literal pattern or flags do not compile; the text is the run time's (Regular expressions) |
'x' is already defined inline; declare it 'inline int x;' to share it | a plain declaration of an inline variable another unit shares |
argument 1 of 'run_sql' is not a constant: <the host's refusal> | an argument the host declared constant (constant arguments) is not a literal, a const of one, or a ternary, + or array literal of them |
the format text of 'print' has 2 placeholder(s) for 1 argument(s) | a print or format literal whose placeholders and arguments do not pair up (also names {2}, past its 1 argument(s) and never uses argument 1 of its 2 argument(s)) |
'to_string' expects 0 argument(s), got 1: the method hides the function 'to_string' here; call that one as ::to_string(...) | a bare call in a method finds the class's member first; the member refuses the arguments and the function it hides takes them |
push changes a call result in place and nothing reads it: 'a.list' returns by value | An in-place builtin called on an array, a map or a string a script function returns by value. An element store into one: cannot assign to a call result: 'a.list' returns by value. |
'n' is a static member of class 'C': name it C::n | A static read, stored or called through an instance (c.n, this.n, c.bump()). A method's bare n or C::n names it. |
Run-time errors (what catch sees)
var dynamic = "hello"; // var: the checker cannot see what it holds
try {
int x = dynamic; // so this fails at run time, where catch sees it
} catch (e) {
print("caught: {}", e);
}caught: Cannot initialize 'x' (declared int) with stringRun-time texts lead with the action that failed, or with the builtin or member that refused it; names are quoted. catch (e) receives an error that reads as the text alone. An uncaught error's what() on the host side is the same text alone, with no category or location appended (only an error raised without a text of its own reads as its category's name). Every text is byte-identical on both backends.
Error kinds
A caught error's e.kind is one of the error_kind:: names (each is a string holding its own name), decided by the error's code, so a script tells errors apart without reading their text:
| Kind | Errors |
|---|---|
error_kind::null_receiver | a member read or a call through null |
error_kind::no_member | a member or static member the value does not have |
error_kind::name | an undefined variable or class |
error_kind::not_callable | a call of a value that is not a function |
error_kind::arguments | an argument count, a constructor or an overload that does not match, an ambiguous call |
error_kind::type | a typed slot, parameter, return, element, key or cast that refuses the value |
error_kind::arithmetic | integer overflow, division or modulo by zero, a non-numeric operand |
error_kind::bounds | an array or string index out of range |
error_kind::key_not_found | a read of a map key that is not there |
error_kind::container | an element taken from an empty array |
error_kind::reference | a reference, handle or capture that cannot bind |
error_kind::limit | the call-depth cap, the memory cap's first raise |
error_kind::host | an exception a C++ function raised: a host function or a library builtin |
error_kind::other | every other run-time error |
The catalog, by family:
Null receiver
| Text | When | Fix |
|---|---|---|
Cannot access 'x' on null ('v' is null) | Member read through a null variable. | Null-check, or assign an object. |
Cannot call 'push' on null ('v' is null) | Method call through a null variable. | Null-check, or assign an object. |
Cannot call 'x' on null ('f(...)' returned null) | Method call on a null call result. | Null-check the result. |
Cannot access 'x' on null | Member read through an unnamed null receiver. | Null-check the receiver. |
No such member
| Text | When | Fix |
|---|---|---|
Vec2 has no member 'x' | Neither the class nor its bases declare the name. | Fix the name, or declare the member. |
int has no member 'foo' | No such method on the builtin type; all read alike (string has no member 'foo'). Load error where the checker knows the receiver is an int, float, bool or char. | Use a listed method (Builtin methods). |
map has no member 'foo'; read the key as ["foo"] | Dot access names no map method. Keys use []; only enum values use a dot (Direction.north). Load error where the checker sees a map. | Write m["foo"], or fix the method name. |
map has no member or key 'foo' | Dot access on an enum value table, where the checker cannot see the enum, names neither a map method nor a value. | Fix the value name. |
parent class Base has no member 'x' | super::name names nothing the base declares. | Name a base member. |
class C has no static member 's' | C::s names no static of the class or its bases. | Declare the static, or fix the name. |
Cannot call 'helper' while 'A' is under construction: only a class derived from it declares it | Constructor or field initializer calls what only a derived class declares. | Declare it in this class, or call after construction. |
Cannot call 'helper' while 'A' is under destruction: only a class derived from it declares it | Destructor calls what only a derived class declares. | Declare it in this class, or do the work in the derived destructor. |
Cannot assign to 'x': Vec2 has no such member | Store to an undeclared member. | Declare the field, or fix the name. |
Cannot assign to member of non-object type | Member store through null or a primitive. | Assign an object first, or null-check. |
'C' is a class; use C::s | Dot after a class name. | Write a static as C::s. |
No value 'v' in enum 'E' | E::v names no value of the enum. | Use a declared value. |
Not a function
| Text | When | Fix |
|---|---|---|
Cannot call 'x': it is an int, not a function | Called variable holds a non-function. | Call a function, or rename the variable hiding it. |
Cannot call 'f': it is null, not a function | Called variable holds null. | Assign a function or lambda first. |
Cannot call a value of type int: it is not a function | Computed callee (an element, a call result) is not a function. | Call only functions. |
Cannot call null: it is not a function | Computed callee is null. | Null-check first. |
Arity and overloads
| Text | When | Fix |
|---|---|---|
'fib' expects 1 argument, got 2 | Call passes more or fewer arguments than declared. | Pass the declared count, or add parameter defaults. |
lambda expects 1 argument, got 0 | Lambda gets more or fewer arguments than declared. | Pass the declared count. |
No overload of 'dot' takes (int, string) | No overload accepts the argument types. | Match an overload, or declare one. |
No constructor of 'Vec2' takes (string) | No constructor accepts the arguments. | Match a constructor, or declare one. |
No constructor found for class 'X' | new X() where X names no class. | Define or import the class, or fix the name. |
Ambiguous call to 'hit' with (int, int): 'hit(int, float)' and 'hit(float, int)' match equally | Two overloads match equally. | Convert an argument (cast<float>(x)) so one wins. |
'f(var& x)' differs from 'f(var x)' (line 1) only by reference; overloads must differ in parameter types or count | Two overloads differ only by reference; the text names the second definition, then the line of the first. | Differ in parameter types or count, or keep one. |
Too many initializers for 'Bare': it has 1 field, got 2 | Aggregate initialization passes more values than fields. | Pass at most one value per field. |
Base 'Cat' has no default constructor; call super(...) | Constructor omits super(...); the base has no default constructor. | Add : super(args). |
Class 'Car' must construct base 'Radio': it has no default constructor | Initializer list omits a later base with no default constructor. | Name that base with its arguments. |
Class 'Car' has no base 'Wheel' to construct | Initializer list names a non-base class. | Construct only own bases. |
Class 'Car' constructs base 'Engine' twice | Initializer list names a base twice. | Construct each base once. |
No matching parent constructor found for super() | super(...) passes arguments; the base declares no constructor. | Drop the arguments, or declare a base constructor. |
push() takes exactly one argument | Wrong argument count to a builtin method; all read NAME() takes .... | See Builtin methods. |
size() takes no arguments | Arguments to a builtin method that takes none. | Pass none. |
remove() argument must be an integer | Wrong argument type to a builtin method; all read NAME() argument must be .... | Pass the listed type. |
math::wrap expects 3 arguments | Wrong argument count to a math function; all read math::NAME expects .... | See the API signatures. |
math::sqrt expects a numeric argument | Non-number to a math function. | Pass an int or a float. |
The typed boundary
| Text | When | Fix |
|---|---|---|
Cannot pass string to parameter 'n' of 'fib' (declared int) | Argument does not convert to its typed parameter. | Pass the declared type, or convert. |
Cannot return string from 'f' (declared -> int) | Returned value does not fit the declared return type. | Return that type, or change the declaration. |
Cannot assign string to 'x' (declared int) | Other type stored into a typed variable. A shared_ptr<T> takes a handle to T or a derived class, never a weak_ptr; a weak_ptr<T> keeps T through every store. | Assign the declared type, convert (weak.lock() for a shared_ptr), or declare var. |
Cannot initialize 'x' (declared int) with string | Typed declaration initialized with another type. | Use the declared type, or convert. |
Cannot assign string to field 'f' of C (declared int) | Other type stored into a typed field. | Assign the declared type. |
Cannot assign string to static 's' of C (declared int) | Other type stored into a typed static. | Assign the declared type. |
Cannot convert B to A (no suitable single-argument constructor) | B object reaches a slot typed A; A has no constructor taking a B. | Pass an A, or add that constructor. |
Cannot store string in array<int> | Other element type stored into a typed array. | Store the element type. |
Cannot initialize 'r' (declared float&) with int | Typed reference binds another type. | Bind a variable of that type, or declare auto&. |
Cannot yield string from 'g' (declared -> int) | Coroutine yields a value its declared type does not take. | Yield the declared type. |
Cannot initialize 'w' (declared weak_ptr<Foo>) with Foo | weak_ptr initialized from a value, not a handle. | Use a shared_ptr<Foo>. |
Cannot pass string to an int parameter | Parameter type check fails with no parameter name known. | Pass the declared type. |
Numeric conversion
| Text | When | Fix |
|---|---|---|
Cannot convert float to integer: non-finite or out of range | NaN, infinity or a float outside the int64 range reaches cast<int>, math::itrunc or math::ifloor; the destination is not written. | Check finite and in range first. |
Cannot store 256 in array<uint8> (0..255) | array<uint8> store outside 0..255. | Stay in range, or mask with & 255. |
Cannot store -129 in array<int8> (-128..127) | array<int8> store outside -128..127. | Stay in range. |
Containers
| Text | When | Fix |
|---|---|---|
Cannot push 'string' to array<int> | push, resize, assign, fill or array<T>(n, v) adds another type to a typed array. | Use the element type. |
array size cannot be negative | array<T>(n), resize, assign or reserve gets a size below 0. | Pass 0 or more. |
resize() size must be an int | resize (assign, reserve alike) gets a non-int size. | Pass an int (cast<int> a float). |
Cannot assign 'string' to element of type 'int' | Other type stored into an element. | Store the element type. |
Cannot use 'int' as a key of type 'string' | Other key type on a typed map. Load error for a subscript whose key and declared key type the checker knows. | Use the declared key type. |
hash() of a map key must return an int | Class keying a map through hash() returns a non-int from it. | Return an int. |
Array 'auto' requires homogeneous elements - type mismatch at index 3 | auto array literal mixes element types. | Use one type, or declare var or array<var>. |
Map 'auto' requires homogeneous values | auto map literal mixes value types. | Use one type, or declare var values. |
Cannot iterate int: not an array, map, string or coroutine | Range-for over a non-iterable value. | Iterate one of the listed types. |
Cannot bind array to [k, v]: a structured binding takes a map entry or a value with first and second | Structured binding gets neither a map entry nor a value with first and second. | Bind a map entry; destructure an array with auto [a, b] = arr. |
map() requires a function argument | map() gets a non-function. | Pass a function or lambda. |
Cannot insert 'string' into array<int> | insert adds another type to a typed array. | Use the element type. |
Cannot insert at index 9 of an array of size 5 | insert index past the end. | Use 0 to size(). |
Cannot reduce an empty array without an initial value | reduce on an empty array with no initial value. | Pass an initial value. |
Destructuring requires an array on the right-hand side | auto [a, b] = value gets a non-array. | Destructure an array. |
Handles and references
| Text | When | Fix |
|---|---|---|
reset(h) takes a handle (new T(), make_shared<T>() or a shared_ptr), not C | p.reset(h) where h is a value or a primitive (load error when the checker sees it). | Pass a handle (p.reset(new C())), or assign into the object (p = C()). |
Cannot reset shared_ptr<C> to a shared_ptr<U> | p.reset(h) where the class of h is neither that of p (for a var, of the held object) nor a subclass. | Reset to a handle of that class or a derived one. |
reset() takes at most one argument | shared_ptr reset gets two or more arguments; on a weak_ptr any argument raises reset() takes no arguments. | Call reset() or reset(h). |
Cannot initialize shared_ptr directly from weak_ptr | shared_ptr initialized from a weak_ptr. | Use weak.lock(). |
Cannot create weak_ptr from value-semantic object. Use shared_ptr<T>. | weak_ptr<T>(v) of a value. | Make the object with new; take the weak_ptr from its handle. |
Cannot pass non-lvalue to reference parameter | Temporary (a literal, a by-value call result) reaches a & parameter that needs a variable, unrejected at load: one the function writes through, or any reference parameter of a lambda called through a variable. A & parameter only read binds a temporary (Value Semantics). A call returning T& (script function or host) is an lvalue and binds. | Pass a variable holding the value. |
Cannot bind non-lvalue default to reference parameter 'p' | Reference parameter default names no storage; raised at the call using the default. | Pass an argument, or default to a variable. |
Cannot take reference of non-lvalue expression (...) | Non-const reference binds an expression naming no storage; the parenthesis lists what a reference binds. | Bind a variable, field or element, or declare const T&, which holds a temporary while the reference lives. |
Function with reference return type must return an lvalue reference | T& function returns an expression naming no storage: a temporary, a by-value call result, a map key with no entry (a read never inserts). | Return a variable, field, element or existing entry, or return by value. |
Cannot take reference of undefined variable | Reference names a nonexistent variable. | Declare it first, or fix the name. |
Reference variable must be initialized | Reference declared without an initializer (int& r;). | Initialize with a variable, field or element. |
Reference to a removed array element | Element reference used after the array shrank past it (pop, erase, clear). The reference names an index: after an insert or reorder it names the element then at that index. | Rebind after shrinking, or keep the index. |
Reference to a removed field | Field reference used after a hot reload removed the field from the class. | Rebind after the reload. |
Reference target is null | Reference binds, or is read through, a reference with no target. | Bind a live variable, field or element. |
Reference to a removed map entry | Reference used after its map entry was erased (array element and field alike). | Rebind after the removal, or keep the entry. |
Cannot iterate a string by reference: its chars are not lvalues (use for (auto c : s)) | for (auto& c : s) over a string. | Use for (auto c : s). |
A host function returning std::weak_ptr<T> is not supported; return shared_ptr<T> | Script calls a host function bound to return std::weak_ptr<T>. | Return shared_ptr<T> from it. |
Cannot assign to a reference to 'C': its binding has no operator= | Assignment through a reference to a host object whose binding lacks "=". | Bind "=" on the host side, or assign fields one by one. |
Const
| Text | When | Fix |
|---|---|---|
Cannot assign to 'MAX': it is read-only | Store to a const variable. | Drop const, or store into a copy. |
Cannot assign to 'p.x': it is read-only ('p' is const) | Field store through a const object. | Drop const, or use a non-const copy. |
Cannot call 'bump' on 'c': 'c' is const and 'bump' writes its receiver (line 5) | Field-writing method called on a const object. | Drop const, or use a non-const copy. |
Cannot call 'push' on 'xs': 'xs' is const and 'push' changes it | Container-changing builtin method called on a const one: listed as mutate in the API reference, or handing elements to a callback that writes through the & parameter an element binds (for_each, find, reduce, ...). | Drop const, or use a non-const copy. |
Cannot bind 'r' (declared int&) to const 'a'; declare it const int& or auto& | Non-const reference binds a const variable. | Declare const int& or auto&. |
Cannot pass const 'a' to parameter 'x' of 'inc': 'inc' writes through it (line 1) | const variable passed to a reference parameter the function writes. | Pass a non-const copy, or stop writing the parameter. |
Cannot cast const 'a' to int&: cast<int>(a) copies it | Reference cast of a const variable. | Copy with cast<int>(a). |
Cannot share const 'p' as shared_ptr<P>; copy it into a variable first | const value shared where a shared_ptr<P> is wanted. | Share a non-const copy. |
Cannot return const 'a' from 'f' by reference (declared -> int&); return it by value | Reference-returning function returns a const variable. | Return by value. |
Assignment targets
| Text | When | Fix |
|---|---|---|
Cannot assign to a literal | Assignment to a literal. | Assign to a variable, field or element. |
Cannot assign to an array literal | Assignment to an array literal (a map literal alike). | Assign to a variable, or destructure: auto [a, b] = arr. |
Cannot assign to a call result: 'get' returns by value | Store to the result of a by-value function. | Return -> T&, or store into a variable. |
Cannot assign to x++: it yields the old value | Store to a postfix increment or decrement. | Assign to the variable. |
Cannot assign to the result of '+': it is a temporary value | Store to an operator result. | Store into a variable. |
Cannot assign to the result of cast<float>(x): it is a temporary value (cast<float&>(x) names the variable) | Store to a value cast. | Store through cast<float&>(x). |
Cannot assign to 'x': it is read-only | Store to a getter-only host property or a host namespace entry. | Use the host setter, or keep the value in a script variable. |
Cannot assign to a conditional: its arm '5' names no storage | Store to c ? a : b with an arm that is not a variable, field or element. | Make both arms name storage, or assign in an if. |
Cannot assign to 'this' | Store to this. | Assign fields one by one. |
Cannot assign to this expression: it names no storage | Target names no variable, field or element. | Assign to a variable, field or element. |
Cannot assign to rvalue expression | Store, compound assignment or ++ on an element of a temporary. | Store the temporary in a variable first. |
Cannot assign to a member of a temporary | make().x = 1 on a class or math value returned by value, a constructor call or an operator result. | Store the value in a variable first. |
Methods as values
| Text | When | Fix |
|---|---|---|
'heal' is a method of Player; call it with player.heal() or bind it with reflect::bind(player, "heal") | Script method used as a value, not called. | Call it, or use reflect::bind. |
'push' is a method of array; call it with arr.push() or wrap the call in a lambda | Builtin method used as a value, not called. | Call it, or wrap the call in a lambda. |
filter changes xs in place and returns nothing; array::filter(xs, f) returns a filtered copy | Result used of a builtin method that changes its receiver in place and returns nothing (auto ys = xs.filter(f), xs.filter(f).map(g)); on a var, raised when the call runs, before any change. The copying twin follows when there is one (In place or a copy). A parallel worker that never saw the text says filter changes its receiver in place and returns nothing. | Call it as a statement, or call the twin. |
Casts
| Text | When | Fix |
|---|---|---|
Cannot cast 'X' to 'T' | cast<T> of a value that does not convert to T. | Cast between convertible types. |
Cannot cast 'int' to 'char' | cast<char> of an int outside 0..255. | Cast 0 to 255. |
Cannot cast 'null' to 'T&' | Reference cast of null. | Null-check first. |
Cannot cast 3 to 'E': not one of its values | cast<E> of an int that is no value of the enum. | Cast a declared value. |
Names
| Text | When | Fix |
|---|---|---|
Undefined variable 'x' | Name the checker could not rule on (var-typed paths, dynamic imports) names nothing at run time; or top-level code reads a global, constructs a class or takes a function as a value before its declaration (top-level order). | Declare the name, or fix its spelling. |
'x' is declared extern but never defined (an 'inline' declaration creates it) | Used extern variable (a qualified one too: the text names 'game::tick') has no definition. | Define it, have the host register it, or declare it inline. |
'f' is declared extern but never defined | Used extern function f; (or extern function ns::f;) has no definition. | Define it, or have the host register it. |
'x' is already defined inline; declare it 'inline int x;' to share it | Plain definition meets an inline one of the same name. | Declare it inline too. |
'x' is already defined (not inline) in <file>; declare every definition 'inline' to share it | inline definition meets a plain one in another file. | Declare every definition inline. |
Undefined variable 'x' (not a field of 'this') | Name in a method is no local, field of the class or global. | Declare the name, or fix its spelling. |
Class 'C' not found | Run-time class name names no defined class. | Define or import the class, or fix the name. |
reflect::construct: unknown class 'Nope' | reflect::construct gets a non-class name. | Pass a defined class name. |
Cannot capture undefined variable 'x' | Capture list names a nonexistent variable. | Declare it first, or fix the name. |
Lambda capture list does not capture enclosing name 'v'; name it, or use [this], [=] or [&] | Lambda body uses an enclosing local missing from its capture list. | Name it there, or use [this], [=] or [&]. |
Namespace 'jai' is reserved for the engine (C++-registered only) | Script declares into the jai namespace. | Use another namespace. |
'this' can only be used inside methods | this outside a method. | Use it only in methods, constructors and destructors. |
Cannot call super() - class has no base class | super(...) in a class with no base. | Remove the call, or add a base. |
Definitions
At load the strict checker reports the class texts, and the method texts with a lowercase first letter.
| Text | When | Fix |
|---|---|---|
Base class 'X' not found | Base name is not a defined class. | Define or import the base first, or correct the name. |
Class 'D' inherits 'A' through both 'B' and 'C' (diamond inheritance is not supported) | One base reached through two paths. | Give each base one path. |
Method 'm' marked as override but no base class method found | override method overrides nothing. | Remove override, or use a base method name. |
Method 'm' redefines 'Base::m': mark it 'override' | Base method redefined without override. | Mark it override, or rename it. |
'g(int a)' is already defined (line 1) | Signature defined twice in one run of one program text; another execute or a re-run unit replaces it instead. | Rename one, or change its parameters. |
Function 'f' already exists in namespace 'ns'. Use 'override' keyword to replace it. | Namespace function of an existing signature defined again without override. | Rename one, or mark the replacement override. |
'f' is already defined with parameters () | Same-file namespace reopening redefines one of its functions; the strict checker reports it at load. | Define it once, or rename one. |
Arithmetic
| Text | When | Fix |
|---|---|---|
Integer overflow in '+' | Int result outside int64 ('-', '*', '/', unary '-' alike), an atomic<int>'s fetch_add and fetch_sub included. | Use smaller values or a float; math::wrap_add wraps on purpose. |
Integer overflow in '+=' | Compound assignment int result outside int64, on any target (variable, field, element, map entry, atomic<int>); '-=', '*=', '/=' alike. | Use smaller values or a float; math::wrap_add wraps on purpose. |
Integer overflow in '++' | ++ steps an int past int64 ('--' below it), on any target, atomic<int> included. | Check the bound first, or step a float. |
Bitwise & requires integer operands | & operand is not an int or char (|, ^ alike) and no class operator takes it; at load when both operand types are known. | Convert the operands to int (cast<int>). |
Left shift requires integer operands | << operand is not an int or char and no class operator takes it; at load when both operand types are known. | Convert the operands to int. |
Right shift requires integer operands | >> operand is not an int or char and no class operator takes it; at load when both operand types are known. | Convert the operands to int. |
Bitwise NOT requires integer operand | ~ operand is not an int or char (a char promotes to its int) and no class operator takes it; at load when its type is known. | Convert the operand to int. |
Unary minus requires numeric operand | Unary - operand is not a number, char (promotes to its int) or math value and no class operator takes it; at load when its type is known (-true). | Negate an int or a float. |
Unary plus requires numeric operand | Unary + operand is not a number, char (promotes to its int) or math value (returned as a copy) and no class operator takes it; at load when its type is known. | Apply it to an int, float, char or math value. |
Division by zero | / or /= divides an int by an int zero (a float operand gives an IEEE infinity or NaN). | Check the divisor first. |
Modulo by zero | % or %= of two ints takes a zero divisor. | Check the divisor first. |
Shift amount must be between 0 and 63 | Shift count negative or above 63. | Keep the count from 0 to 63. |
Cannot apply '+=' to int and string | Compound assignment has no rule for its operand types (every compound and destination); at load when both types are known (cannot apply '+=' to 'int' and 'bool'). | Convert an operand so the types match. |
Cannot apply '++' to string | ++ or -- targets a value that is not a number or char, or an object whose class binds no ++ of that form; at load when its type is known. | Increment an int, float or char (promotes to its int); a C++ class binds operator++() as [](T&) and operator++(int) as [](T&, int64_t). |
Cannot convert 'string' to 'char': a char takes a string of exactly one character | String of a length other than one reaches a char slot: variable, parameter, field, return, array<char> element or map<char, V> key. A literal fails the load with the same reason (as a subscript key, with this text). | Index the character (s[0]), or keep the slot a string. |
cannot apply '+' to 'string' and 'int': stringify it with to_string(x) or `${x}` | Number, bool or null beside a string under +, either order (the text names both types); the same text at load when both types are known. | Make the other side text: to_string(x), or a template string `${x}` (splices are text). |
Invalid operands for + operator | + has no rule for its operand types and no user operator+ takes them (-, *, /, % alike: numbers and chars take every one; a string on either side takes +). At load when both operand types are known. | Convert an operand, or declare the operator in the class. |
No bound 'Money + int': bind operator "+" taking int on Money, or convert the operand | Bound C++ class declares the operator, but no bound overload takes the other operand. | Bind an overload taking that type, or convert the operand. |
No bound 'int - Money': bind operator "-" as (int, Money) on Money, or convert the operand | Number or other non-class value left of a bound class operator, and the class binds no reflected form taking it (for a comparison the text asks for "<=>" taking int on Money). | Bind the reflected form, .method("-", [](int64_t l, const Money& r) { ... }), or convert the operand. |
Invalid operands for < operator | < compares types with no ordering, and no user operator takes them. | Compare values of one comparable type, or declare the operator in the class. |
Vectors
| Text | When | Fix |
|---|---|---|
math::vec3 expects one vector of 3 components | One-argument vector constructor gets a lone scalar or a vector of another dimension. | Pass a same-dimension vector, or every component. |
math::color expects one color | One-argument math::color gets a non-color. | Pass a color, or every component. |
math::vec4 expects a narrower vector followed by scalars that complete 4 components | Wrong number of scalars after a narrower vector. | Follow the vector with exactly the missing components. |
math::vec2 expects numeric components | Vector constructor component is not a number. | Pass ints or floats. |
math::vec3 expects 0 to 3 arguments | Too many constructor arguments (every vector type, named by its own type and count). | Pass at most one argument per component. |
Integer overflow: the value does not fit an int32 component | Int for an ivec2 or ivec3 component (constructor, component store, arithmetic result, loaded archive) outside int32. A JAISCRIPT_WRAP_ON_OVERFLOW build wraps it instead. | Keep components within int32, or use a float vector. |
Indexing
| Text | When | Fix |
|---|---|---|
Array index 5 out of bounds for array of size 3 | Array index negative or not below size(). | Index from 0 to size() - 1. |
Key "helth" not found in map | Subscript reads a key the map lacks (typed and var maps alike): a plain read, a compound assignment or increment (reads first), ?? or ??= on the entry, a reference bound to it, or a chain link such as m[a][b]. The key shows as inside a container: string or char quoted, number plain. In a parallel body, a key whose text the program never made shows as Key not found in map. | Ask contains(k) first, or read with get(k) (null) or get(k, fallback); m[k] = v inserts. |
Array index must be an integer | Array subscript is not an int. | Index with an int (cast<int> a float). |
String index 4 out of bounds for string of size 4 | String index negative or not below size(). | Index from 0 to size() - 1. |
Subscript can only be used on arrays, maps, or types with [] operator | [] on a value that is no container and declares no operator[]. | Subscript a container, or declare operator[] in the class. |
Cannot pop from empty array | pop on an empty array. | Check size() first. |
Cannot get front of empty array | front on an empty array (back alike). | Check size() first. |
front() called on empty string | front() on an empty string (back() alike). | Check size() first. |
String index must be an integer | String subscript is not an int. | Index with an int. |
Cannot assign int to a string character (it takes a char or a string) | String element store of a value that is neither a char nor a string. | Store a char (cast<char>(n)) or a string. |
Strings, bytes, time
| Text | When | Fix |
|---|---|---|
code() needs a string of exactly one UTF-8 character | code() on a string that is empty or holds more than one character. | Call it on a one-character string. |
to_char expects 0..255, got 300 | to_char gets an int outside 0..255. | Pass an int from 0 to 255. |
string::repeat count cannot be negative | string::repeat gets a negative count. | Pass a count of 0 or more. |
Unsupported format spec ':q' in format string | format() or print placeholder {:spec} / {n:spec} names a spec outside the subset (a template string ${x:q} is the load-time Unsupported format spec ':q' in template string). | Use the subset, [[fill]align][+| ][0][width][,|_][.precision][d|x|X|b|f|e|g]; write {{ for a literal brace. |
Format text has 2 placeholder(s) for 1 argument(s) | Placeholders and arguments do not pair up: the {} count differs from the argument count; a {n} names none (Format text names {2}, past its 1 argument(s)); no placeholder takes one (Format text never uses argument 1 of its 2 argument(s)). A literal mismatch fails the load; one in vformat raises. | Pair every placeholder with an argument; text without a placeholder joins its arguments. |
format spec ':.2f' does not apply to string value | Spec sign, zero padding, grouping, precision or type does not fit the value (a string under a numeric spec, a float under x). | Pass a value of the kind the spec formats, or convert it first. |
to_bytes(4) out of bounds for string of size 3 | to_bytes offset or count past the end of the string. | Keep offset and count within size(). |
string::from_bytes expects bytes (0 to 255): element 3 is 256 | string::from_bytes element outside 0..255. | Pass bytes from 0 to 255. |
Cannot read int32 at position 10: 4 bytes wanted, 2 remain | Binary reader reads past the end of its data. | Check the bytes remaining first. |
Cannot write 256 as uint8 (0 to 255) | Binary writer value outside the range of its width. | Write values in range, or use a wider width. |
Cannot read null-terminated text at position 0: no NUL terminator in the remaining 3 bytes | Reader text() finds no NUL before the end of its data, or before the engine binary scan limit (the text then names the limit). | Read a counted field with text(n), or check the data holds a terminator. |
Cannot write text: the text holds a NUL at byte 1 | Writer text(s) gets a text with a NUL inside. text(s, n): Cannot write text: the text is 6 bytes, longer than text(3) for a text longer than its field; Cannot write text(s, -1): a count cannot be negative for a negative count. | Remove the NUL, or give the field room for the text. |
Cannot pack field 'pos': the map has no 'pos' | Layout packs a map lacking one of its fields. | Give the map every field the layout names. |
binary::layout expects name, width pairs, got 3 arguments | binary::layout gets an odd number of arguments (reader, writer and layout texts in full: Binary data). | Pass a name and a width per field. |
time::date: month must be 1 to 12, got 13 | time::date month outside 1..12. | Pass a month from 1 to 12. |
web::url_encode expects a string | web:: function argument is not text (every one reads web::NAME expects a string; web::build_query expects a map). | Pass the kind the web:: library names. |
web::build_query: the value of 'm' is a map | web::build_query value is neither text, a number, a bool, null nor an array of those (... is an array holding more than text, numbers and bools, ... is not text, a number, a bool or an array of them). | Pass text, numbers and bools, or an array of them. |
web::parse_url: 'http://example.org:99999/' has a malformed host or port | Port is not a number from 0 to 65535, or an IPv6 host bracket does not close. | Correct the URL, or catch the error for outside input. |
spider::catch: no HTTP transport installed: the host provides one with engine::http_transport() | spider:: call in an engine whose host registered the client and installed no transport (every call reads spider::NAME: ...). | Install one with engine::http_transport. |
spider::catch: http://127.0.0.1:1/: cannot connect to 127.0.0.1:1 | Exchange failed: the transport reason, or timed out after N s, more than N redirects, a redirect from https to http (URL) is refused, redirected to 'L', which is not an http or https URL. | Catch it; raise the timeout or redirects option for a slow or far server. |
spider::catch: 'ftp://example.org/' is not an http or https URL | URL is relative, of another scheme, or has no host. | Pass an absolute http or https URL. |
spider::catch: unknown option 'retries' (method, headers, timeout, type, redirects) | Option unknown or of the wrong kind (option 'method' takes GET, HEAD, POST, PUT, PATCH, DELETE or OPTIONS, option 'timeout' takes a number of seconds above 0, option 'redirects' takes an int from 0 to 100, 'N' is not a header name, header 'N' holds a line break or NUL in its value, header 'Host' is the transport's to set (Content-Length, Transfer-Encoding and Connection too), type "form" takes a map of fields). | Use the options HTTP with spider:: lists. |
Response.header: 'X Y' is not a header name, or its value holds a line break or NUL | spider::Response.header(name, value) gets a name that is not an HTTP token, or a value holding CR, LF or NUL. | Use a token name (letters, digits and !#$%&'*+-.^_`|~) and a one-line value. |
Response.set_cookie: option 'path' holds a ';' or a control character | set_cookie or clear_cookie option value would add an attribute (also 'N' is not a cookie name (letters, digits and !#$%&'*+-.^_`|~), and option 'N' is unknown or of the wrong kind (max_age, expires: int; path, domain, same_site: string; secure, http_only: bool)). | Pass a token name and plain option values; the cookie value may hold anything (it is percent-encoded). |
time::date::add_days expects a whole number | Calendar step (add_days, add_months, add_years) gets a fraction, an infinity or NaN. | Pass a whole number. |
time::date::add_days is out of range | Calendar step or duration in add_* past ±1015 (days, months, years, or a duration in milliseconds). | Step by a smaller amount. |
time::date::format: unknown placeholder '${bogus}' | Date format string names an unknown placeholder. | Use a placeholder from Time. |
Regular expressions
| Text | When | Fix |
|---|---|---|
regex: missing ')' to close the group at position 0 | Pattern does not parse; every such text reads regex: PROBLEM at position N, N counting characters from 0 (the texts in full: The syntax). | Correct the pattern at that position. |
regex: the backreference '\1' at position 3 is not supported: matching runs in linear time, without backtracking | Pattern uses a construct that needs backtracking: a backreference, a lookahead or lookbehind, an atomic group, a possessive quantifier or recursion, each named alike. | Match more and test the parts in script, or capture and compare the groups. |
regex: unknown flag 'g' (the flags are i, m, s and x) | Flags hold a letter that names no flag (regex: flag 'i' appears twice for one written twice). | Pass only i, m, s and x (find_all and replace cover every match). |
regex: the class at position 0 holds 'é', a character of more than one byte, but a string matches by bytes (match a utf8_string, or write its bytes as \xNN) | Pattern whose class holds a multi-byte character searches a string. | Search a utf8_string, or name the bytes with \xNN. |
regex: the pattern is too large to match a string (its program passes 250000 instructions) | Counts multiply past the engine program limit ((a{1000}){1000}). | Use smaller counts, or a loop in script. |
regex: the replacement names group 2 at position 0, but the pattern has 1 group | Replacement refers to a group the pattern lacks (regex: the replacement names group 'x' at position 0, which the pattern does not have by name; regex: a '$' at position 1 of the replacement starts no group reference (write $$ for a dollar sign) for a stray $). | Refer to a group the pattern has; write $$ for a dollar sign. |
regex: the replace function must return a string, got int | Function replacement returns a non-text value. | Return a string (to_string(v)). |
regex::match has no group 5 (it has 4 groups) | Match asked for a group past its last (regex::match has no group named 'day' for an unknown name). | Ask for group 0 to m.size() - 1, or a name the pattern gives. |
regex: search position 4 is outside the text (0 to 3) | search(text, start) start before 0 or past the end. | Start between 0 and the size of the text. |
regex: search expects a string or utf8_string, got int | Regex call searches a non-text value (regex: the pattern must be a string, got int for the pattern). | Pass a string or a utf8_string. |
Random numbers and crypto
| Text | When | Fix |
|---|---|---|
random::next_int range is inverted (max < min) | next_int(lo, hi) gets hi below lo (random::next_float range is inverted (max < min) for floats). | Pass the bounds low first. |
random::next_float bounds must be finite | next_float(lo, hi) gets an infinite or NaN bound. | Pass finite bounds. |
random::pick expects a non-empty array | pick gets an empty array or a value that is no array. | Check the array size first. |
random::shuffle expects an array | shuffle gets a value that is no array. | Pass an array variable. |
random::next_bytes count cannot be negative | next_bytes (or crypto::random_bytes, crypto::random_hex: crypto::random_bytes count cannot be negative) gets a negative count. | Pass a count of 0 or more. |
random::state expects 64 hex digits, not all zero | Store into state (a load included) is not 64 hex digits, or is all zeros. | Store a value read from state. |
crypto::random_int range is inverted (max < min) | crypto::random_int(lo, hi) gets hi below lo. | Pass the bounds low first. |
crypto::random_bytes: the operating system's secure random generator failed | System generator refused to answer (the text names the function: random_hex, random_int, password_hash, or random: ... for random()). | Retry; the platform entropy source is unavailable. |
crypto::password_hash cost must be 4 to 31 | password_hash cost outside 4..31. | Pass a cost from 4 to 31 (12 by default). |
crypto::password_hash: the password contains a NUL byte | Password holds a 0 byte, which bcrypt cannot read past. | Reject or encode such passwords before hashing. |
Templates
Errors marked at load fail the script that calls template before it runs, as a parse failure listing the template file and line. A scoped template's rules about its text fail at load for a literal text or path, at the first render otherwise (HTML Templates). A template file read with the filesystem privilege off raises Insufficient privilege: filesystem. where the call renders.
| Text | When | Fix |
|---|---|---|
template() argument 2 is not a plain variable name: the template sees each argument by its name, so name the value first (auto entry = rows[0];) and pass the name | template argument after the path is not a plain name (at load). | Name the value in a local and pass the local. |
template() takes a path, then the names the template sees | template() called with no arguments (at load). | Pass the template path first. |
template() path is not a string | Path known at run time is not text. | Pass the path as a string. |
scoped_template(path, map) takes 2 arguments, got 1 | scoped_template or scoped_template_text gets another count of arguments (at load). | Pass the path or the text, then the map. |
raw() takes 1 argument | raw in a template gets another count of arguments. | Pass the one value to splice unescaped. |
template() names 'entry' twice | One name passed twice (at load). | Pass each name once. |
template file 'page.spider' not found | Path resolves to no file (at load for a literal path, at the render otherwise; ... cannot be read when the file cannot be opened). | Check the path: it resolves from the calling script file, as an import path does. |
a template sees only the names passed to it and the program's names; 'this' is not one of them (pass a local instead) | Template names this or super (at load). | Pass the value the template needs as a name. |
a lambda in a template captures 'total', which the template does not see | Lambda in a template captures a name the template was not given (at load). | Pass the name to the template. |
unexpected text after the template body | Splice ends the body early, such as ${::g}, whose : starts a format spec. | Parenthesize the expression: ${(::g)}. |
a template splice is null: ${title} at page.spider:3 | Splice in a template or a scoped template evaluates to null (at load for a literal null). | Give the value, or write ${title ?? ""}. |
Insufficient privilege: scoped_template. | scoped_template or scoped_template_text on an engine whose host disabled jai::scoped_template. | The host enables jai::scoped_template. |
scoped_template: '../secret.txt' names a parent directory (..) | Scoped template path has a .. segment. | Name the file from inside a template folder. |
scoped_template: 'C:/secret.txt' is not a path relative to its template directories | Scoped template path is absolute, names a drive or a UNC share, or starts with / or \. | Name the file relative to a template folder. |
scoped_template: 'link/secret.txt' leaves its template directory through a link | The only file the path names lies outside its folder once links and junctions resolve. | Keep templates inside the folders engine::scoped_template_dirs() lists. |
template file 'card.spider' not found in the scoped template directories | No template folder holds the path. | Put the file in a template folder, or the host adds the folder. |
a scoped template sees only args: 'secret' is not available | Scoped template names anything but args and its lambda parameters, a global or ns::name included. | Put the value in the map. |
a scoped template calls only built-in methods and the string::, array:: and map:: functions: 'leak(...)' is not allowed | Scoped template calls a free or global function, or a function of a namespace other than string::, array:: and map:: (or a script's own function there). | Compute the value before the render and pass it in the map. |
a scoped template does not call function values: 'call(...)' is not allowed | Scoped template calls a function value (one in the map, a lambda, a call result). | Pass the value the function would return. |
raw() is not allowed in a scoped template: its output is always escaped | Scoped template calls raw. | Render HTML the program trusts with template and pass the result. |
a scoped template is one expression over args: an assignment is not allowed | Scoped template assigns, or uses this, new, a cast, throw, yield, include or import (a scoped template does not assign: '++' is not allowed for ++ and --). | Keep the template to reads and built-in methods over args. |
a scoped template's lambda takes plain parameter names | Scoped template lambda has typed, reference or defaulted parameters or a capture list (a scoped template's lambda is one expression (x => expr) for a block body). | Write x => expr. |
a scoped template does not call a method of a class instance, a host object or a function: its code could run | Scoped template reaches code through a value: a method of an instance or host object (... does not apply an operator to ..., ... does not splice ..., ... does not call a built-in method over ..., ... does not test ..., ... does not index ..., ... does not key a map by ..., ... does not pass a built-in method ... for the other ways). | Pass plain data: strings, numbers, arrays and maps. |
a scoped template reads the plain fields of a script class instance: 'secret' is not one | Scoped template reads a host property, a field with a user accessor, or a member an instance does not have. | Pass the value in the map. |
a scoped template indexes arrays, maps and strings | Scoped template indexes another kind of value. | Index an array, a map or a string. |
template()'s output passed its 10485760-byte limit: the host raises it with engine::template_output_limit | template render would pass engine::template_output_limit bytes (10 MiB by default). | Render less, or the host raises the limit (0 is none). |
a scoped template's output passed its 1048576-byte limit: the host raises it with engine::scoped_template_output_limit | Scoped render would write more than engine::scoped_template_output_limit bytes (1 MiB by default). | Render less, or the host raises the limit (0 is none). |
scoped_template's second argument is a map | scoped_template or scoped_template_text second argument is not a map. | Pass a map. |
a scoped template's lambda runs only inside its render | Lambda a scoped template made is called after the render ended. | Use the lambda inside the template. |
Files
| Text | When | Fix |
|---|---|---|
Failed to open include file: helpers.jai | include of a file that cannot be opened. | Correct the path, or add the file. |
Failed to open import file: creature.jai | import of a file that cannot be opened. | Correct the path, or add the file. |
parse failed: <path>:LINE:COL: error: MESSAGE | A file a dynamic import(expr) reaches does not parse. | Fix the parse error the message names. |
Path outside the confined roots: ../secret/creds.txt | include, import, resolve_path or file_data finds its file only outside the host's confine_paths folders. | Move the file under a confined root, or the host adds its folder. |
include 'part.jai' includes itself or nests too deep | A literal include in a function body reaches a file it is already splicing, or splices over 32 files deep. A parse error at the include's path. | Break the cycle, or import the shared file. |
import(...) takes paths: strings or arrays of strings | import(expr) gets neither a string nor an array of strings. | Pass a string, or an array of them. |
Insufficient privilege: filesystem. | include, import, template, scoped_template or a file builtin, with the filesystem privilege disabled by the host. | The host enables jai::filesystem, or the script leaves files alone. |
Cannot read file 'saves/world.gqw': not found | The runner's read_file, or crypto::sha256_file, cannot read the path (not a file, permission denied, cannot be opened, read failed alike). | Correct the path; check the file exists and is readable. |
Archives
| Text | When | Fix |
|---|---|---|
Invalid shared_ptr ID: 4 | from_json, from_binary or from_base64 of a handle whose object the save never wrote. | Load unedited data a matching save wrote. |
Exceptions
| Text | When | Fix |
|---|---|---|
Bare throw with no active exception thrown. | throw; outside a catch block. | Rethrow inside catch, or throw a value. |
Coroutines
| Text | When | Fix |
|---|---|---|
Cannot resume a completed coroutine | next() on a finished handle, or a next(n) step past the end. | Check done() before next(). |
Coroutine is already running | A coroutine's own body resumes it. | Resume it only from outside. |
next() takes 0 or 1 arguments ([count]) | next() with more than one argument. | Pass nothing, or a step count. |
next() count must be an integer | next(n) with a non-int count. | Pass an int. |
next() count must be at least 1 | next(n) with a count below 1. | Pass 1 or more. |
advance() takes 1 argument (the variable that receives the yielded value) | advance with no argument, or several. | Pass the one receiving variable: while (h.advance(v)). |
Coroutine has not finished: result() is available once done() is true | result() before the body has run to its end. | Drive it to the end (while (h.advance(v)), a range-for), or test done(). |
result() takes no arguments | result() with arguments. | Pass none. |
done() takes no arguments | done() with arguments. | Pass none. |
coroutine_handle has no member 'resume' | A method the handle lacks, such as resume. | Resume with next(). |
Sleep
| Text | When | Fix |
|---|---|---|
sleep expects a number of seconds | sleep gets neither an int nor a float. | Pass seconds as a number. |
Parallel bodies
| Text | When | Fix |
|---|---|---|
parallel_for: TEXT at LINE:COL | A body is refused before the loop runs. parallel_transform: and parallel_reduce: lead those builtins' texts. | Change the code at LINE:COL as TEXT says, or loop serially. |
parallel_for: reference declaration would alias enclosing state 'g' (captured reads are read-only) at 4:12 | A reference bound to a variable outside the loop, or to a member of one. | Bind references to the element or a local; read enclosing state by value. |
parallel_for: assignment target is not allowed in a parallel body at 3:9 | An assignment, or a reference binding (reference declaration target alike), to storage the body cannot prove its own, such as a call's result. A conditional of lvalues names the arm it picks, each arm held to the rules. A member of a call returning a reference (pick().a = 1) raises member store must root at a local variable in a parallel body. | Assign to the element, a local or out[i]. |
parallel_transform: first argument must be an array | parallel_transform input is not an array (parallel_reduce: first argument must be an array alike). | Pass an array. |
parallel_for: can only iterate an array | The range is not an array. | Iterate an array. |
parallel_for: zipped array 2 is not an array | A zipped value is not an array. | Zip arrays. |
parallel_for: zipped array 2 has 1 elements, the range has 3 (zipped arrays must have the same length) | A zipped array's length differs from the range's. | Zip arrays of the range's length. |
parallel_for: zipped array 2 is the range itself | A zipped array is the range's own array. | Zip a separate array. |
parallel_transform: fn must be a script-defined function | fn is not a script function or lambda. | Pass a script function or lambda. |
parallel_transform: fn may not take its element by reference (elements are detached copies) | fn takes its element by reference. | Take it by value. |
parallel_transform: expected (array, fn) or (array, fn, weight_fn) | parallel_transform with another argument count (parallel_reduce: expected (array, init, step, combine) alike, under [[warn_mode]]). | Pass the arguments the call takes. |
parallel_transform: weight_fn must take exactly one parameter by value | weight_fn does not take one parameter by value. | Declare one by-value parameter. |
parallel_transform: weight_fn must return a number (element 2) | The weight function returns neither an int nor a float for that element (must return a finite non-negative number for a negative, infinite or NaN weight). | Return a non-negative number for every element. |
parallel_transform: weight_fn must return a finite non-negative number (element 0) | weight_fn returns a negative, infinite or NaN weight for that element. | Return a finite, non-negative number. |
parallel_transform: weight_fn failed for element 2: TEXT | The weight function raised a run-time error for that element. Its own throw arrives as thrown. | Fix the weight function as TEXT says. |
parallel_for: a nested parallel_for is not allowed in a parallel body (the outer loop's iterations already run in parallel: write the inner loop as for) at 4:9 | A parallel_for statement in the body. | Write the inner loop as for. |
parallel_for: nested parallel regions are not supported | A parallel loop starts while another runs, from code the statement could not see (a host callback). parallel_transform: and parallel_reduce: alike. | Start the inner loop after the outer one. |
parallel_for: lambdas are not allowed in a parallel body | The body writes a lambda. | Call a named function, or a global that holds the lambda. |
parallel_for: yield is not allowed in a parallel body | The body yields. | Yield outside the loop. |
parallel_for: coroutine method 'g' is not allowed in a parallel body | The body calls a coroutine method. | Call it outside the loop. |
parallel_for: function declarations are not allowed in a parallel body | The body declares a function (namespace and enum declarations alike). | Declare it outside the loop. |
parallel_for: include/import is not allowed in a parallel body | The body includes or imports. | Include or import outside the loop. |
'break' is not allowed in a parallel_for body: iterations are independent; use 'continue' for the next element | A loop-level break in the body. A parse error. | Use continue or a top-level return for the next element. |
parallel_for: cast<T&> is not allowed in a parallel body | cast<T&> in the body. | Remove the reference cast from the body. |
parallel_for: call target 'pick' is not a function known at admission time | A call through a local of the enclosing function. | Call a named function, or a global that holds a function or lambda. |
parallel_for: call target 'f' is not statically resolvable in a parallel body | A call through a parameter or a local of the body. | Call a named function, or a global that holds a function or lambda. |
parallel_for: captured name 'twice' is a function value (only direct calls to statically-known functions are allowed in a parallel body) | A function read as a value, to store it or to pass it. | Call the function directly. |
parallel_for: static member calls are not allowed in a parallel body | A call to a static method of a script class. | Call a free or namespace function, or call it outside the loop. |
parallel_for: time:: calls are not allowed in a parallel body (engine-owned clock) at 3:41 | A time:: call in the body. | Read the time before the loop; capture the value. |
parallel_for: random::seed cannot be called on captured 'loot' in a parallel body (seed before the loop: each element draws its own stream from it) at 5:46 | The body, or code it reaches, reseeds a captured random. | Seed before the loop. Each element's stream comes from one draw of the captured generator, the same at every worker count (In parallel loops). |
parallel_for: crypto::sha256_file cannot be called in a parallel body (it reads a file) at 3:9 | The body, or code it reaches, hashes a file. | Hash files before or after the loop. The other crypto:: functions run in a body. |
parallel_for: method 'bump' on class 'Pt' is not admitted in a parallel body (only single-overload methods of the element's class or its bases) at 4:33 | A method call the region cannot admit. | Call only single-overload methods defined on the element's own class. |
parallel_for: class 'Pv' has non-public members — its methods are not admitted in a parallel body | A method call on a class that has private members. | Call methods only of classes with no private members. |
parallel_for: method 'size' must be called as 'this.size()' in a parallel body (a bare call of a C++ base method) | A method the body runs calls a C++ base method by its bare name. | Spell it this.name(). |
parallel_for: body writes enclosing state 'total' at 11:31 | A write to a variable outside the loop. | Write only the element, or total with parallel_reduce. |
parallel_for: method 'push' may mutate captured state 'g' | A mutating or unrecognized method called on captured state (g.push(...)), a captured object included (parallel_for: method 'push' may mutate captured state 'c'). | Call only the read-only builtins on it, or write a local copy or the element. |
parallel_for: cannot iterate captured state 'g' by reference in a parallel body (captured reads are read-only) | for (auto& v : g) over captured state. | Loop with const auto&. |
parallel_for: captured name 'xs' is the container being mutated (reads through it would race the element writes) | A subscript read of the range's own array, or a const T& of it or of an index-written array. | Read the element through the loop variable. |
parallel_for: argument passes enclosing state 'grid' to reference parameter 'g' of 'peak' (captured reads are read-only; 'peak' only reads it: declare it const array<int>& g) at 6:40 | A variable outside the loop passed to a function's or method's non-const reference parameter that the function never writes. | Declare the parameter const T&: it takes enclosing state as a captured read. |
parallel_for: argument passes enclosing state 'grid' to reference parameter 'g' of 'clear', which writes it (captured reads are read-only; pass a copy, or take it as const T& where it is only read) at 6:40 | The same, for a parameter the function writes through. | Pass a copy, or write the element (or an index write) instead. |
parallel_for: reference parameter 'x' of 'bump' defaults to enclosing state 'count', which writes it (captured reads are read-only; pass a copy, or take it as const T& where it is only read) at 6:40 | A call to a function, method or constructor omits the argument for a non-const reference parameter whose default names a variable outside the loop. | Pass a local, or declare the parameter const T& where it is only read. |
parallel_for: '::K' names the program's 'K' and the body also reads a nearer 'K' (a worker holds one variable of a name: rename one) | The body reads both ::K and a local K of the enclosing function. | Rename the local, or read the program's variable through one spelling. |
parallel_for: argument passes enclosing state 'g' by reference (captured reads are read-only) at 3:9 | A variable outside the loop passed to a C++ function's non-const T& parameter. | Pass a copy. A C++ const T& parameter takes it as a read. |
parallel_for: a counted loop must step its variable by a fixed amount toward a bound (write i++, i--, i += k or i -= k) | A counted parallel_for steps another way (i *= 2, a call in the step, a step of another variable). | Step by a constant or a name, or iterate an array of the indices. |
parallel_for: a counted loop compares its variable with a bound (i < b, i <= b, i > b or i >= b) | The condition is missing, tests another variable or uses another comparison. | Compare the loop variable with its bound. |
parallel_for: a counted loop declares one int variable (int i = a or auto i = a) | The counted form declares a non-int, a reference, a const or several variables. | Declare one int variable. |
parallel_for: a counted loop must step toward its bound (i < b and i <= b count up, i > b and i >= b count down) | A constant step moves away from the bound: the serial loop would not end. | Flip the step or the comparison. |
parallel_for: a counted loop's step must not be zero | The step is the constant 0. | Use a nonzero step. |
parallel_for: a counted loop's step of -2 never reaches its bound (the serial loop would not end) | A step read from a name is zero or moves away from the bound, and the loop would run at least once. | Give the step the bound's direction. |
parallel_for: a counted loop's bound is float, not int | The start, bound or step is not an int (a load error when its type is declared). | Convert with int(...) before the loop. |
parallel_for: the body writes the counted loop's variable 'i' (the loop steps it; copy it into a local to change it) at 3:40 | The body assigns the counted variable, or passes it to a reference parameter that writes it. | Copy it into a local. |
parallel_for: method 'scale' of 'Vec3' writes its object, and captured 'sun' is read-only (write a local copy, or the element) at 11:50 | A method that writes its object, called on a captured object or on an object a captured one holds. | Copy it into a local first (Vec3 s = sun;), or keep the state in the elements. |
parallel_for: member store reaches captured state 'cam' (captured reads are read-only) at 4:45 | A store into a field of a captured object. | Write a local copy or the element. |
parallel_for: captured handle 'h' is read through its members and methods only (a worker reads its own view of the object) at 3:40 | A captured shared_ptr used itself: compared, copied or passed. | Read its members, or capture the members needed. |
parallel_for: element 1 reaches the object captured as 'h' (a captured object is read-only, and an element's lane may write it) | An element of the range or of a zipped array is, or holds, an object the body also reads through a captured name. | Read the object through the element, or copy what the body needs before the loop. |
parallel_for: captured name 'bad' is not value-semantic (an object holding a handle, a function, a var field or a C++ object) | A captured object holds what no worker may copy (an object whose class runs a destructor, a script class over a C++ class, a C++ object inside a script object alike). | Capture the fields the body reads. |
parallel_for: captured name 'w' is not value-semantic (object/shared_ptr/weak_ptr) | The body captures a weak_ptr. | Capture the values the body reads. |
parallel_for: member access reaches captured state 'm' (captured reads are read-only, and only a captured object has members) at 3:27 | A member read of a captured value that neither is nor holds an object. | Subscript a container: m["k"]. |
parallel_for: element 0 cannot be proven exclusive (only value-semantic shapes are: primitives, strings, math vectors, value containers, flat value-class instances and distinct handles to them) | Elements bound in place that the region cannot prove exclusive. Raised before the loop runs, without a position. | Iterate value-semantic elements, or distinct handles to them. |
parallel_for: zipped array 2 element 0 cannot be proven exclusive | The same, for an element of a zipped array. | Zip value-semantic arrays, or distinct handles. |
parallel_transform: element 0: value is not value-semantic (object) | An element is not a primitive, a string, a math vector, a value container, a flat value-class instance or a handle to an object no other element names. | Iterate value-semantic elements. |
parallel_reduce: input element 0 cannot be proven exclusive | The same, for an element of a parallel_reduce input that holds objects. | Reduce over value-semantic elements, or distinct handles. |
parallel_reduce: input elements 0 and 1 hold the same object (an object the step reaches through a handle element belongs to one element) | Two handle elements of the input name one object. | Give each element its own object. |
parallel_for: 'Tune::K' is no longer a constant (a parallel body reads only const statics of a primitive value) | A class the body reads a constant of was redefined; the member is no longer a static const of a primitive value. | Keep the member static const, or read it outside the loop. |
parallel_reduce: step names '__jai_reduce_xs', the reduction's own input | The step reads the name the reduction gives its input. | Read the element through the step's parameter. |
parallel_for: elements 0 and 3 hold the same object (an object a body reaches through a handle element belongs to one element) | Two handle elements name one object. | Give each element its own object. |
parallel_for: elements 1 and 2 share one 'random' in field 'brain' (an element owns the C++ values it holds: give each element its own, or capture one from outside the loop) | Two elements hold one bound C++ object (a random, a time::date), in the named field or, with none named, as an element of a value container. Raised before the loop runs. | Give each element its own object, or draw from one random captured from outside the loop. |
parallel_for: elements 2 and 5 write 'out' at the same index 4 | An index write out[i] = v gets one index from two elements. | Give every element a distinct index. |
parallel_for: body touches 'out' beyond the slot it writes, out[i] at 4:20 | The body writes out[i] and reads out elsewhere. | Touch out only at the slot written. |
parallel_for: body writes enclosing state 'out' at an index the body changes at 3:9 | The body writes out[i] and changes i, directly or through a T& parameter that writes it. | Leave the index unchanged. |
parallel_for: captured name 'out' is written at an index but is not an array | The body writes out[i] where out is not an array. | Index-write only into an array. |
parallel_reduce: step must take two parameters (acc, x) | The step does not take two parameters (combine must take two parameters (a, b) alike). | Declare the step as (acc, x), the combine as (a, b). |
parallel_reduce: step must be a script function or lambda | The step is not a script function or lambda. | Pass a script function or lambda. |
parallel_reduce: step takes its element by value (the elements are the input's, never written) | The step takes its element by reference. | Take it by value. |
parallel_reduce: body writes enclosing state 'total' at 3:40 | A step the loop rules refuse raises their text under its name. | Fold into the accumulator, not enclosing state. |
parallel_for: body writes enclosing state 'next_id' in the constructor of 'Tick' at 2:46 | A construction in the body runs code that writes enclosing state (in a field initializer of 'T' and in the destructor of 'H' alike). | Keep constructors, field initializers and destructors the body uses free of enclosing writes. |
parallel_for: C++ class 'Sprite' is marked serial_only_constructor() and cannot be constructed in a parallel body at 3:48 | The body constructs a host class registered serial-only. | Construct it before or after the loop. |
parallel_for: class 'Glowing' has a C++ base 'Sprite' marked serial_only_constructor() and cannot be constructed in a parallel body at 4:49 | The body constructs a script class whose host base is registered serial-only. | Construct it before or after the loop. |
parallel_for: coroutine constructor of class 'Gen' is not allowed in a parallel body at 3:5 | The body constructs a class whose constructor is a coroutine. | Construct it before or after the loop. |
parallel_for: host function 'print' is marked serial_function() and cannot be called in a parallel body at 3:5 | A call to a host function registered serial. | Call it before or after the loop. |
parallel_for: C++ static method 'Sound::play' is marked serial_static_method() and cannot be called in a parallel body at 3:5 | A call to a host static method registered serial. | Call it before or after the loop. |
method 'm' was not admitted in this parallel body (admitted: single-overload public methods of the element's class or its bases touching only element, local, or captured-read state) | A body refused while it runs raises the text alone; here a method call the region cannot admit. | Call only the methods the text admits. |
only direct field access on class instances is allowed in a parallel body | A member read on a class instance that is not a direct field read. | Read fields directly. |
only direct field stores on class instances are allowed in a parallel body | A store into a class instance that is not a direct field store. | Store fields directly. |
body writes enclosing state 'calls' in operator '+' of 'V' at 2:68 | A class operator whose body breaks the parallel body rules. The text is the rule's, as parallel_for: TEXT at LINE:COL gives it, without the prefix, naming the operator and its class. | Make the operator follow the body rules, or loop serially. |
converting constructor of 'T' was not admitted in this parallel body (the statement admits a conversion into a slot it can type) | A conversion into a slot of class T would run its converting constructor where the statement could not type the slot, so the construction was never walked. | Construct explicitly (T(x)), or convert into a slot the body declares. |
A host call failed during a parallel region | A C++ function or method the body calls threw. The engine records no new text while a body runs, so the exception's text is not kept. | Run the call outside the loop to read the exception's text. |
custom operator dispatch on class instances is not admitted in a parallel body | A class operator the statement never walked, such as the global [] operator. | Apply it before or after the loop, or loop serially. |
an assignment that changes an object's class is not admitted in a parallel body | A subclass instance stored into a slot that holds another class. | Keep each object's class unchanged in the body. |
C++ method 'publish' of 'Meter' is marked serial_method() and cannot be called in a parallel body | A call to a host method registered serial. | Call it before or after the loop. |
C++ method 'add' of 'Meter' changes an object this parallel body did not make (only its const methods may run on it) | A non-const host method called on an object the body did not make. | Call only const methods on it, or make the object in the body. |
C++ property 'level' of 'Meter' is stored on an object this parallel body did not make (only an object it made takes stores) | A host property stored on an object the body did not make. | Store only on objects the body makes. |
member 'extra' is stored on a shared object in a parallel body (a body writes its element, out[i] and the objects it made) | A store into an object a host call returned that the program already holds, or into an object one of its fields holds. | Change it before or after the loop, or write the element, out[i] or an object the body makes. |
method 'grow' cannot run on a shared object in a parallel body (an object reached through a host call takes only its C++ class's const methods) | A script method, or a method of an object with no C++ object, called on a shared object. | Call it before or after the loop. |
C++ call 'attach' takes a shared object that holds no C++ object in a parallel body | A shared object with no C++ object passed to a C++ function or method. | Pass it before or after the loop. |
member 'on_hit' of a shared object cannot be read in a parallel body (it holds a function or a weak_ptr) | A read of a shared object's field that holds a function or a weak_ptr. | Read it before the loop; capture the result. |
a host call in a parallel body returned an object one of its iterations owns (an element, or an object a chunk made) | A host call returns an object the region gave to one iteration. | Reach the element through the loop variable. |
a shared object in a parallel body cannot be copied by value: its C++ object has no copy function | A by-value copy of a shared object whose C++ class registers no copy. | Hold it as a shared_ptr, or copy it before the loop. |
parallel_transform: type shape was not pre-warmed at the region barrier (internal) - please report this body shape | A region's thread builds a type the region did not prepare before its threads started (a library defect). | Report the body; a serial loop avoids it. |
parallel_transform: name 'tally' was not pre-warmed at the region barrier (internal) - please report this body shape | A region's thread needs a name the region did not prepare before its threads started (a library defect). | Report the body; a serial loop avoids it. |
Script layers
| Text | When | Fix |
|---|---|---|
Cannot copy C++ object of type 'T': the type is not copy-constructible. Hold it by reference (auto&/var&) or share it (shared_ptr<T>). | A copy of a bound C++ object whose class has no copy constructor (an atomic<int>, a host type that deletes it): a by-value declaration, parameter, return, element or capture. | Hold it by reference (auto&, var&, a T& parameter). A typed slot it converts into (int n = obj;) reads it without a copy. |
Cannot copy an instance of nocopy class 'C': hold it by reference (auto&/var&) or share it (shared_ptr) | A copy of a nocopy class instance. | Hold it by reference (auto&, var&) or share it with a shared_ptr. |
Script layer 'C' was detached (script_layer_detach, or a move to another engine): this handle no longer reaches its object | A handle used after its script layer was detached or moved to another engine. | Stop using the handle, or take a new one. |
extend: 'C' derives the C++ class 'B', which is not the object's C++ class 'O' or one of its bases | extend(obj, C) names a class whose host base does not match the object. | Extend with a class derived from the object's own host class or one of its bases. |
extend: 'C' needs an object to extend | extend(obj, C) or the host's eng->extend(obj, "C") gets null or a non-object. | Pass the object to extend. |
Access
| Text | When | Fix |
|---|---|---|
Cannot access private member 'x' of class 'C' | Read or write of a private member from outside the class and its subclasses. | Make it public, or use a public method. |
Cannot access private constructor of class 'C' | Call of a private constructor from outside the class and its subclasses. | Make it public, or construct through a public static function. |
Limits (catchable)
| Text | When | Fix |
|---|---|---|
Maximum recursion depth exceeded - possible infinite recursion | Active calls reach the cap: 256 (interpreter) or 250,000 (VM) by default (engine::max_call_depth(backend, depth)). | Bound the recursion, or raise the cap in the host. |
Out of memory | The platform refuses a builtin's allocation (crypto::random_bytes(n), s.repeat(n), s.pad_left(n) with n past available memory). | Ask for less, or set engine::memory_cap in the host so the cap's text comes first. |
Nesting too deep for the host thread's stack | Nesting (expressions, statements, lambdas, a long operator chain) deeper than the backend can walk on the host thread's stack. If the parser or checker gets there first, the load fails with nesting too deep for the host thread's stack in the static listing. | Flatten it (name intermediate values, split a long chain), or use a thread with a larger stack. |
Native stack exhausted before the call-depth cap: the host thread's stack bounds this recursion | A recursion on the host thread's own stack nears its end before the call-depth cap, on either backend: interpreter calls, a top-level include reaching its own file again, script reentered from host callbacks. | Bound the recursion, or use a thread with a larger stack. |
The engine stays usable afterwards. On the VM a bare self tail call reuses its frame and adds no depth; the budget stops a runaway tail loop. A value nests as deep as memory allows: deep copy, ==, <=> and the orderings, to_json/from_json and to_binary/from_binary walk work lists, not the host thread's stack.
Limits (terminal: script cannot catch them)
| Text | When | Fix |
|---|---|---|
Script execution budget exceeded - raise engine::execution_budget or break up the work | An execute runs past its execution budget. | Raise engine::execution_budget in the host, or split the work across executes. |
Script execution budget exceeded - raise engine::execution_step_budget or break up the work | An execute takes more budget checks than its step budget. | Raise engine::execution_step_budget in the host, or split the work across executes. |
Run cancelled | The host cancels a suspended run (script_run::cancel, the run's destructor, engine teardown): the run's sleep raises it, every frame unwinds, destructors run. | None in script: the host ends the run. |
Script memory cap exceeded (N bytes) - raise engine::memory_cap or break up the work | The memory cap's second raise in one execute; the first is catchable. | Raise engine::memory_cap in the host, or hold less at once. |
Errors in script the runtime calls. Each raises its own errors: a sort comparator, a filter or remove_if predicate, the other callback helpers, a parallel_for body, a user operator, to_bool() under a condition, to_string() under string +, an accessor or post_load an archive runs.
- The text leaves the builtin or operator unchanged and catchable; uncaught, its trace carries the callback's frames.
- The builtin makes no further calls after the first failure;
sortleaves the array as it was. - An archive load tolerates only a store the member itself refuses (a read-only host property, a value the field's store rejects); that member keeps its constructed value.
A caught error's e.stack and the host's format_stack_trace() list the innermost 64 frames, then one line ... N more frames. A host keeping an error result past 256 newer errors whose text embeds a value reads (the error text is no longer available) in its place.
Texts thrown to the host as jai::runtime_error, never seen by a script:
Call-depth configuration
| Text | When | Fix |
|---|---|---|
max_call_depth requires a built-in backend type | max_call_depth names a backend that is not built in. | Pass the VM or interpreter backend. |
max_call_depth must be between 1 and INT_MAX | max_call_depth gets a depth below 1 or above INT_MAX. | Pass 1 to INT_MAX. |
Cannot change max_call_depth during execution | max_call_depth set while a script runs. | Set it before or after an execute. |
Privileges
| Text | When | Fix |
|---|---|---|
Unknown privilege name | A named privilege no registration defines. | Register it first, or correct the name. |
Invalid privilege flags | Flags hold bits no privilege defines. | Build flags from defined privileges. |
Privilege name must not be empty | A privilege registered or named with an empty string. | Name the privilege. |
Cannot change privileges during a parallel region | A host function called from a parallel body enables or disables a privilege. | Change privileges outside parallel regions. |
reflect::is_enabled expects at least 1 argument | Script calls reflect::is_enabled with no argument. | Pass the privilege to test. |
reflect::is_enabled expects valid privilege flags | Script passes reflect::is_enabled flags no privilege defines. | Pass a defined privilege. |
Callbacks
| Text | When | Fix |
|---|---|---|
Cannot convert a value that is not a function to a std::function callback | convert_script_function gets a non-function value. | Pass a script function or lambda. |
Cannot call a script function after its engine was destroyed | The host calls a converted callback kept past its engine. | Drop the callback before the engine. |
Engine entry points
| Text | When | Fix |
|---|---|---|
Variable 'x' not found | get_variable names a variable no script defined. | Execute the defining script first, or correct the name. |
Failed to open script file: <path> | execute_file cannot open the path. | Correct the path, or add the file. |
set_backend after execute() is unsupported: defined functions and classes capture the executing backend | set_backend after the engine executed code. | Call set_backend before the first execute. |
set_backend(interpreter) is unsupported: this build has no interpreter (link jaiscript_interpreter) | set_backend(backend_type::interpreter) in a program built without the interpreter. | Link jaiscript_interpreter (CMake option JAISCRIPT_INTERPRETER) in the program that compares the backends. |
Suspendable runs
| Text | When | Fix |
|---|---|---|
A run resumes only on the thread that started it | resume() or cancel() on a thread other than the run's start. | Resume on the starting thread. |
The run is running: it resumes only while suspended | Code the run itself is running (a host function under it) resumes or cancels it. | Resume from outside the run. |
The script_run holds no run | resume(), cancel() or result() on a moved-from or default script_run. | Use the script_run the run moved to. |
Cannot start a run of a value that is not a function | start(callable, args) gets a non-function value. | Pass a function value. |
Host records
| Text | When | Fix |
|---|---|---|
Record nesting depth exceeds the host thread's stack | Save or load of host types whose serialize/save/load/load_and_construct recurse natively runs out of stack. | Nest host records less deeply, or use a thread with a larger stack. |
Maximum record nesting depth (1024) exceeded | The same recursion passes 1024 levels where the stack cannot be asked (JAI_MAX_HOST_RECORD_DEPTH); script values nest without limit. | Nest host records less deeply, or raise JAI_MAX_HOST_RECORD_DEPTH. |
Conversions
A host reads a value as a C++ type (as<T>(), as_int(), a bound function's parameter, a bound property's store) and it holds another type. The text names the held type as type_of spells it, then the type asked for. A script that caused the read catches the same text.
| Text | When | Fix |
|---|---|---|
Cannot convert string to int | A string where the host reads an int; the same text names every other pair (float, bool, char, string, array, map, object, function, weak_ptr, script class instance, a math type such as math::vec3). | Pass the type the host reads, or convert in the script first. |
cannot bind a non-const script reference to a C++-bound variable of a different width | A host T& parameter receives a C++-bound variable whose width or signedness differs from T. | Take the parameter by value, or bind a variable of its exact type. |
Values and engines
| Text | When | Fix |
|---|---|---|
Value outlived its engine. | A held_value read after its engine was destroyed. | Drop held values before their engine, or copy the data out first. |
Cannot pass an object from another engine: values cross engines by copy (numbers, strings, math values and arrays and maps of them). | A value handed to another engine holds something only its own engine can name (an object, a function, a pointer). | Pass plain data, or rebuild the object in the receiving engine. |
Archives
Thrown as jai::serialization_error by the C++ archives (from_json, from_binary and the readers under them) when data does not match the loaded type.
| Text | When | Fix |
|---|---|---|
Expected an object in the archive, found an integer | Another kind of value where the loaded type reads a record (an object), an array, a map or an integer. Found side: null, a bool, an integer, a float, a string, an array, an object or nothing in JSON; the end of the data or marker N in binary. | Load with the type that wrote the data, or correct the data. |
Archive value 300 does not fit int8 | An integer field's value outside its C++ type's range, or a number with a fraction (1.5, inf) read into an integer field. The field is named by width: int8 to int64, uint8 to uint64. | Widen the field, or correct the data. |
Property 'x' not found during deserialization | A serialize/load reads a key the record lacks. | Check has_property first for a key older files lack (Versions and missing fields). |
Dangling shared_ptr reference: {"$id": 4} names an object this archive never defined (T) | A handle refers back to an id no earlier data defined: a hand-edited or reordered file. | Keep the file in the order written, or save it again. |
Unknown polymorphic type: Name | A $type no registrar, property_owner or binding in the archive's engine names. | Register or bind the type before loading. |
Polymorphic type 'Name' is not a registered subtype of requested base 'Base' (...) | A $type names a registered type unrelated to the pointer's base. | Declare the relation, or correct the data. |
Expected '$val' for polymorphic shared_ptr | A handle with a $type and no $val. | Save the data again. |
Saved programs
A malformed .jaibite is refused before anything runs; every text starts jaibite: .
| Text | When | Fix |
|---|---|---|
jaibite: truncated header | The file ends inside its header. | Save from source again. |
jaibite: not a jaibite file (bad magic) | The file does not start with the jaibite magic. | Load a file a jaibite save wrote. |
jaibite: unsupported format version M (expected N) | Saved by a build of another format version (M; this build reads N). | Save from source with this build. |
jaibite: corrupt varint | A malformed encoded number. | Save from source again. |
jaibite: AST nesting too deep | The saved syntax tree nests deeper than the writer or loader can walk on the thread's stack. | Save or load on a thread with a larger stack, or flatten the nesting. |
jaibite: type nesting too deep | A saved type nests deeper than the writer or loader can walk on the thread's stack. | Save or load on a thread with a larger stack, or flatten the nesting. |
jaibite: missing required block statement node | A required block is missing. | Save from source again. |
jaibite: expected block statement node | A node that must be a block is something else. | Save from source again. |
jaibite: invalid program: local slot out of range | A structure check fails. The other jaibite: invalid program: ... checks: a local count the file's syntax does not account for, a break outside a loop or switch (a parallel_for body is not one), a continue outside a loop, a malformed parallel_for. | Save from source again. |
jaibite: corrupt count | Every other load text names the damaged or foreign part: unexpected end of data, corrupt count, corrupt string index, corrupt type_info, corrupt token type, corrupt literal value, corrupt capture default, corrupt member visibility, corrupt check mode attribute, corrupt node tag N, trailing bytes after declarations, expected expression/statement/declaration node, missing required expression/statement/declaration node, destructuring name/slot count mismatch. | Save from source again. |
jaibite: cannot serialize unresolvable symbol id | A save meets a name its engine cannot spell. jaibite: unserializable AST node type N: a node the format cannot encode. | Save from source in the engine that parsed it. |
Parse errors that name the JaiScript spelling. Each word is recognized only where parsing would otherwise fail, so a script using it as a name (var goto = 5;) keeps working:
| Written | Text |
|---|---|
int size() const { } | JaiScript has no const methods: a method that writes no field already works on a const object |
const int f() { } | JaiScript has no const return types: return by value (the caller gets a copy) or by T& |
array<const int> | const qualifies a declaration, not a type: write const array<int> xs |
shared_ptr<const T> | JaiScript has no pointer to const: const shared_ptr<T> keeps the handle from rebinding, and the object stays writable |
const_cast<T&>(x) | JaiScript has no 'const_cast': a const stays const; copy it (auto x = c) for a writable value |
constexpr int N = 4; | JaiScript has no 'constexpr': a const whose initializer is a constant expression is folded at compile time: const int N = 4; |
mutable int cache; | JaiScript has no 'mutable' fields: every field of a const object is read-only; keep state that must change behind a shared_ptr field |
[n]() mutable { ... } | A lambda takes no 'mutable': its copied captures are already its own, writable and kept between calls |
x = const 5 | 'const' starts a declaration: const int x = 5;; const int x;: const 'x' needs an initializer: const int x = value;; for (const : xs): a range-for's const variable needs a name: for (const x : xs) |
x instanceof int | 'instanceof' tests a class, and 'int' is a builtin type: compare type_of(x) == "int"; with nothing after it: expected a class name after 'instanceof' |
virtual void f() | JaiScript has no 'virtual': every method dispatches dynamically; mark the subclass's method 'override' |
delete x; | JaiScript has no 'delete': an object is freed when its last owner lets go; set the handle to null to release it |
new int[5] · new T; | 'new' makes one shared object (new T(args)); an array is a value: array<T> a = []; · 'new T' needs its constructor's parentheses: new T() or new T(args) |
goto done; | JaiScript has no 'goto': use break, continue, return or a flag |
super.speak() | A base class method is called with super::name(args), not super.name |
super(5); in a body | super(...) constructs the base only in a constructor's initializer list (: super(args)); call a base method with super::name(args) |
&x | JaiScript has no address-of '&x': bind a reference with auto& r = x, or pass x to a T& parameter |
class B extends A | JaiScript has no 'extends': derive with class Name : Base |
a === b · a !== b | JaiScript has no '===': == never converts between types, so a == b already compares without coercion ('!==' alike) |
function* g() | JaiScript has no 'function*': a generator is a coroutine, coroutine name(...) { yield v; } |
[...a, 3], f(...args) | JaiScript has no spread '...': join arrays with array::concat(a, b) (a.concat(b) appends in place), and pass arguments one by one |
Other parse errors in the same voice:
enum value 'b' names 'c', which is not an earlier value of 'F'(an enumerator's initializer is a constant integer)array<T>(...) takes a size and an optional value, not 3 arguments'uint8' is an array element type only (array<uint8>, one byte per element); a single byte is an int'??=' needs a variable, a member or an element whose index runs no code; store anything else in a local firstA map entry binds two names, the key and the value: for (auto [k, v] : m)operator! cannot be declared: !v is !to_bool(v); declare bool to_bool()Nested function 'helper' cannot use enclosing local 'y': a named function captures nothing; use a lambda with a capture ([&], [=] or [y]) or function(...) / =>a class cannot be declared inside a function body (a class is a program-scope type); declare it at program scope or import its file'a' names g::a by a using declaration; a variable cannot be declared with that name