Errors & error codes
Every diagnostic code (E1000–E5001) Falx's lexer, parser, runtime, module loader and CLI can produce -49 in total. Each one below was actually triggered against the interpreter to confirm its exact message and behaviour, not inferred from source alone.
error.
The registry supports a warning level and reserves a note level, but every code that's actually wired up today uses error -there is no warning-only diagnostic in Falx yet. So "how serious is it" has one answer for almost everything here: encountering any of them stops the program immediately, the same as any other halting error, aside from the handful explicitly marked Reserved or Internal guard below, which can't currently occur at all.
Variables, constants & functions
E1000 Undefined variable
Fatal -halts execution
Message shape: undefined variable '<name>'
Something tried to read a variable that was never declared with let or const anywhere in scope -including a simple typo in a name that was declared.
output(doesNotExist);
→ e.flx:1:8: error[E1000]: undefined variable 'doesNotExist'
How to prevent it: Declare the variable before using it, and double-check spelling/case -Falx names are case-sensitive.
E1004 Assign to constant
Fatal -halts execution
Message shape: Cannot assign to constant '<name>'
A variable declared with const was reassigned. const only blocks reassignment of the variable itself -see the note below if the value is a container.
const x = 1;
x = 2;
→ e.flx:2:3: error[E1004]: Cannot assign to constant 'x'
How to prevent it: Use let if the variable needs to change. Note that const only protects the binding, not the contents -const arr = [1]; arr.push(2); is perfectly legal, since you're mutating the array, not reassigning arr itself.
E1005 Variable already exists
Fatal -halts execution
Message shape: Variable '<name>' already exists
A second let/const declared a name that already exists in the same scope. This is not the same as shadowing in a nested block (see Control flow) -that's allowed. This is specifically re-declaring in the same environment.
let x = 1;
let x = 2;
→ e.flx:2:5: error[E1005]: Variable 'x' already exists
const x = 1;
let x = 2;
→ e.flx:2:5: error[E1005]: Variable 'x' already exists
How to prevent it: Just assign to the existing variable (x = 2;, no let/const) instead of re-declaring it, or pick a different name.
E1031 Function already exists
Fatal -halts execution
Message shape: Function '<name>' already exists
The same restriction as E1005, specifically for func declarations: a function name can't be declared twice in the same scope.
func f = define() { return 1; }
func f = define() { return 2; }
→ e.flx:2:6: error[E1031]: Function 'f' already exists
How to prevent it: Give the second function a different name, or remove the duplicate declaration.
E1022 Duplicate parameter
Fatal -halts execution
Message shape: Duplicate parameter '<name>'
Two parameters in the same function declaration share a name.
func f = define(a, a) { return a; }
→ e.flx:1:18: error[E1022]: Duplicate parameter 'a'
How to prevent it: Rename one of the parameters -every parameter name in a single define(...) must be unique.
Types, operators & conversions
E1002 Runtime error (generic)
Reserved -not currently raised
Message shape: varies
The registry's fallback definition for the base FalxRuntimeException class itself, for whenever a runtime problem doesn't match any of the more specific exceptions below. Verified unreachable: nothing in the current interpreter raises the base class directly -every runtime failure uses one of the specific codes on this page instead.
How to prevent it: Not applicable -you should never actually see this code today. If you do, it likely means a future interpreter change introduced a new, not-yet-specific error path.
E1006 Cannot multiply by value
Fatal -halts execution
Message shape: Cannot multiply <value> by <value>.
The * operator was used on a combination of types that don't define multiplication with each other (numbers, and number-by-string repetition, are the only combinations that work -see Arithmetic).
output({} * 2);
→ e.flx:1:9: error[E1006]: Cannot multiply {} by 2.
How to prevent it: Only multiply numbers, or a number by a string (which repeats the string). Maps, arrays, booleans and null don't support *.
E1007 Cannot divide by value
Fatal -halts execution
Message shape: Cannot divide <value> by <value>.
The / operator was used where one side isn't a number. This is different from dividing by zero -see E1003 below for that specific case.
output(true / 2);
→ e.flx:1:11: error[E1007]: Cannot divide true by 2.
How to prevent it: Only divide numbers.
E1003 Division by zero
Fatal -halts execution
Message shape: cannot divide by zero
Both sides of / were numbers, but the divisor was 0 (integer or float -both are caught).
output(5 / 0);
→ e.flx:1:9: error[E1003]: cannot divide by zero
output(1.0 / 0);
→ e.flx:1:11: error[E1003]: cannot divide by zero
How to prevent it: Check the divisor before dividing: if (b != 0) { output(a / b); }.
E1008 Cannot add with value
Fatal -halts execution
Message shape: Cannot add <value> to <value>
The + operator was used on a combination that doesn't define addition. Numbers add with numbers; strings concatenate with almost anything (see Concatenation) -but note the message's order: this specifically fires when the left side doesn't know how to combine with the right.
output(true + 5);
→ e.flx:1:14: error[E1008]: Cannot add 5 to true
How to prevent it: Put a string first if you're building text ("" + value), or make sure both sides are numbers for arithmetic.
E1019 Cannot subtract from value
Fatal -halts execution
Message shape: Cannot minus <value> from <value>
The - operator (binary subtraction, not unary negation) was used where one side isn't a number.
output(true - 1);
→ e.flx:1:14: error[E1019]: Cannot minus 1 from true
How to prevent it: Only subtract numbers. There's no string/array "remove" meaning for - in Falx.
E1009 Cannot mod with value
Fatal -halts execution
Message shape: Cannot mod <value> by <value>
The % operator was used where one side isn't a number.
output(5 % "x");
→ e.flx:1:11: error[E1009]: Cannot mod 5 by x
How to prevent it: Only use % between two numbers.
E1010 Cannot compare to
Fatal -halts execution
Message shape: Cannot compare '<type>' with '<value>'
One of < <= > >= was used on a type that doesn't define ordering -only numbers support these. This is a common surprise: unlike ==, ordering comparisons don't work on strings at all. See Comparison.
output("a" < "b");
→ e.flx:1:12: error[E1010]: Cannot compare 'string' with 'b'
output(true > false);
→ e.flx:1:13: error[E1010]: Cannot compare 'boolean' with 'false'
How to prevent it: Only use < <= > >= between two numbers. For strings, compare via a property like .length, or write your own comparison logic.
E1011 Cannot convert to type
Fatal -halts execution
Message shape: Cannot convert '<type>' to <target>
Something needed a boolean and the value had no boolean conversion. This is the error you get from while, assert(), and &&/|| when handed a number, array, map, null, tuple, or struct instance -none of those define a boolean conversion. Critically, plain if does not raise this for the same values; see the callout below.
while (5) { output(1); }
→ error[E1011]: Cannot convert 'number' to bool
if (5) {...} raise this? Because if never calls the boolean conversion at all -it only recognises literal true, and silently treats everything else (including a number that would raise E1011 in a while) as false. See Truthiness.How to prevent it: Give these positions an actual boolean -a comparison or equality check -rather than a raw value. Full explanation, including why if behaves differently from while/&&/|| here, on Truthiness.
E1015 Cannot evaluate value
Fatal -halts execution
Message shape: Cannot evaluate '<left>' and '<AND/OR>'.
Raised by &&/|| specifically (logical expressions), when the left-hand side has no boolean conversion -a sibling of E1011 for this one operator pair. The message is a little cryptic: the second quoted part is literally the word AND or OR, not a value.
output(5 && true);
→ e.flx:1:10: error[E1015]: Cannot evaluate '5' and 'AND'.
How to prevent it: Make sure both sides of &&/|| are actual booleans (comparisons, equality checks, or other logical expressions) -see Logical operators.
E1030 Cannot invert value
Fatal -halts execution
Message shape: Cannot invert '<value>'
Unary ! was used on a value with no boolean conversion.
output(!5);
→ e.flx:1:8: error[E1030]: Cannot invert '5'
output(!null);
→ e.flx:1:8: error[E1030]: Cannot invert 'null'
How to prevent it: Only negate an actual boolean, or a string that is exactly "true"/"false" (see Truthiness) -not a number, array, map or null.
E1026 Unexpected type
Internal guard -not reachable from Falx code
Message shape: varies (internal)
An internal check inside several built-in string/array/map methods, guarding against the method's this not being the type it expects. It's wired to real call-sites in the interpreter -but every one of those call-sites is only reachable if a method got detached from its owner and rebound to a different type, which isn't something Falx script syntax can actually do (a bound method stays bound to the value it came from, even if you store it in a variable first). Verified unreachable by trying the obvious ways to trigger it.
How to prevent it: Not applicable from normal Falx code today.
Properties, indexing & collections
E1012 Cannot access property
Fatal -halts execution
Message shape: '<value>' has no property '<name>'
Dot access read a field or method name that doesn't exist on a struct instance or module. Note this is struct/module-specific behavior -the same situation on a map just returns null instead of raising.
struct P { x; }
let p = P();
output(p.notAField);
→ e.flx:3:10: error[E1012]: 'p' has no property 'notAField'
How to prevent it: Check the struct's field/method names for typos, and remember structs are strict about this while maps are lenient.
E1014 Cannot set property
Fatal -halts execution
Message shape: '<value>' has no settable property '<name>'
A dot-assignment tried to set a field that isn't declared on the struct. Unlike maps (where m.newKey = x always works, adding the key), structs only allow assigning to fields that were declared in the struct { ... } body.
struct P { x; }
let p = P();
p.newField = 5;
→ e.flx:3:12: error[E1014]: 'P' has no settable property 'newField'
How to prevent it: Declare every field the struct will ever need up front in its body -you can't add new fields to an instance later the way you can with a map.
E1013 Object is not indexable
Fatal -halts execution
Message shape: '<type>' is not indexable
[] was used on a type that has no concept of indexing -only strings, arrays, maps and tuples are indexable.
let x = 5;
output(x[0]);
→ e.flx:2:9: error[E1013]: 'number' is not indexable
let s = "hi";
s[0] = "h";
→ e.flx:2:2: error[E1013]: 'string' is not indexable (raised when assigning to a string index -strings are immutable)
How to prevent it: Only index strings, arrays, maps and tuples -and remember strings reject writing to an index even though reading works (see E1028 below for the same restriction on tuples).
E1016 Invalid index (generic)
Reserved -not currently raised
Message shape: Index '<index>' is invalid for structure with size '<size>'
The base definition shared by the array/string/map-specific variants below. Verified unreachable on its own: every actual out-of-range index goes through one of E1017/E1018/E1034 instead, each of which fills in a more specific type name.
How to prevent it: Not applicable -see E1017, E1018 or E1034 instead.
E1017 Invalid index on array
Fatal -halts execution
Message shape: Index '<index>' is invalid for array with size '<size>'
An array (or, confusingly, a tuple -see the warning below) was indexed with a number outside its valid range.
let a = [1, 2, 3];
output(a[5]);
→ e.flx:2:9: error[E1017]: Index '5' is invalid for array with size '7'
len() of the array's printed representation (e.g. "[1,2,3]", 7 characters) rather than its element count, because of how the interpreter re-wraps this specific exception. It also mislabels a tuple as "array" for the same reason, since tuples reuse this array bounds-check internally. The bounds check itself is still correct -only the numbers/type name quoted in the message text are wrong. Trust the location the diagnostic points at, not the size or type it states.How to prevent it: Check .size before indexing with a computed value, and remember negative indices count from the end (a[-1] is the last element) rather than being automatically out of range.
E1018 Invalid index on string
Fatal -halts execution
Message shape: Index '<index>' is invalid for string with size '<size>'
A string was indexed with a number outside its valid range. Unlike E1017, the size quoted here is accurate -strings happen not to trigger the representation bug described above.
let s = "hi";
output(s[10]);
→ e.flx:2:9: error[E1018]: Index '10' is invalid for string with size '2'
How to prevent it: Check .length before indexing with a computed value.
E1034 Invalid index on map
Fatal -halts execution
Message shape: Index '<key>' is invalid for map with size '<size>'
Raised specifically by .remove(key) when the key doesn't exist -not by reading a missing key with [] or dot access, both of which just return null (see Maps). This asymmetry is worth remembering: reading a missing key is silent, removing one is not.
let m = {a: 1};
m.remove("nope");
→ e.flx:2:10: error[E1034]: Index 'nope' is invalid for map with size '1'
How to prevent it: Check .contains(key) before calling .remove(key) if the key might not be present.
E1023 Invalid index type
Fatal -halts execution
Message shape: Cannot convert '<value>' to int
An array or tuple was indexed with something that isn't (and can't be converted to) an integer -arrays and tuples only accept numeric indices, unlike maps which accept any key type.
let a = [1, 2, 3];
output(a["x"]);
→ e.flx:2:9: error[E1023]: Cannot convert 'x' to int
How to prevent it: Use a number to index an array or tuple. If you want string keys, use a map instead.
E1029 Destructuring count mismatch
Fatal -halts execution
Message shape: Cannot destructure a tuple with <n> elements into <m> variables
A tuple destructuring pattern had a different number of variables than the tuple has elements. Falx never truncates or pads -the counts must match exactly.
let (a, b) = (1, 2, 3);
→ e.flx:1:5: error[E1029]: Cannot destructure a tuple with 3 elements into 2 variables
let (a, b, c) = (1, 2);
→ e.flx:1:5: error[E1029]: Cannot destructure a tuple with 2 elements into 3 variables
How to prevent it: Match the pattern's variable count to the tuple's .size exactly, checking it first if it's not a fixed literal.
E1028 Immutable value
Fatal -halts execution
Message shape: Cannot modify an immutable tuple
Something tried to write to a tuple -index-assignment is the only way this currently happens, since tuples have no mutating methods to call in the first place.
let t = (1, 2);
t[0] = 9;
→ e.flx:2:3: error[E1028]: Cannot modify an immutable tuple
How to prevent it: Use an array instead of a tuple if you need to change elements after creation -tuples are permanently fixed once built.
E1033 Cannot hash object
Fatal -plus a known crash path, see below
Message shape: Cannot hash <type> with value of '<value>'
A mutable, unhashable value (an array or map) was used as a map key. This is only handled cleanly in one of its two code paths -see the warning below.
let m = {a: 1};
m.remove([1, 2]);
→ e.flx:2:10: error[E1033]: Cannot hash array with value of '[1,2]' -clean, via .remove()
m[[1,2]] = "x"; crashes with a raw, unhandled Python traceback (TypeError: catching classes that do not inherit from BaseException is not allowed) instead of this clean error, because of a broken exception-handling clause in the map's index-assignment code. .remove([1,2]) on the same map, by contrast, raises E1033 correctly, as shown above. Avoid the index-assignment form entirely with non-string/number keys until this is fixed upstream.How to prevent it: Never use an array or map as a map key -use a string or number key instead, converting first if necessary.
Functions & calls
E1020 Object not callable
Fatal -halts execution
Message shape: '<type>' is not callable
Parentheses () were applied to a value that isn't a function, struct constructor, or other callable.
let x = 5;
x();
→ e.flx:2:2: error[E1020]: 'number' is not callable
How to prevent it: Check the value actually holds a function before calling it -a common cause is a typo that resolves to the wrong variable.
E1021 Invalid argument count
Fatal -halts execution
Message shape: Expected <n> arguments, but got <m>
A function was called with the wrong number of arguments. Falx checks this strictly for ordinary functions -no extra arguments are silently dropped, and no missing ones are silently filled with null. Note this is not the same for struct constructors, which are deliberately lenient (see Structs) -this code covers plain function/method calls only.
func add = define(a, b) { return a + b; }
output(add(1));
→ e.flx:2:12: error[E1021]: Expected 2 arguments, but got 1
func add = define(a, b) { return a + b; }
output(add(1, 2, 3));
→ e.flx:2:12: error[E1021]: Expected 2 arguments, but got 3
How to prevent it: Match the call site to the function's declared parameter count, counting any parameters with default values as optional (see default parameters).
E1024 Recursion depth exceeded
Fatal -halts execution
Message shape: Maximum recursion depth of <n> exceeded
A function called itself (directly or indirectly) more than the configured limit -currently 500, genuinely tracked and enforced (verified by recursing past it).
func f = define(n) {
if (n <= 0) { return 0; }
return f(n - 1);
}
output(f(600));
→ e.flx:3:14: error[E1024]: Maximum recursion depth of 500 exceeded
How to prevent it: Restructure deep recursion into a loop where possible, or confirm the recursive call actually has a reachable base case (an infinite-recursion bug is the most common real-world cause). See Recursion.
E1032 Expected value
Fatal -halts execution
Message shape: varies -e.g. "Empty seperator on split method"
A built-in method received an argument that was structurally valid (right type) but semantically empty/unusable -currently only raised by "...".split(sep) when sep is an empty string.
output("abc".split(""));
→ e.flx:1:20: error[E1032]: Empty seperator on split method
How to prevent it: Pass a non-empty separator to .split().
Control flow & iteration
E1025 Object not iterable
Fatal -halts execution
Message shape: '<value>' is not iterable
foreach was used on something that isn't iterable -only arrays, maps and strings can be iterated (see foreach).
foreach (x: 5) { output(x); }
→ e.flx:1:1: error[E1025]: 'x' is not iterable
How to prevent it: Only use foreach on an array, map, or string.
E1027 Assertion failed
Fatal -halts execution
Message shape: Assertion failed<message, if given>
assert(condition, message?)'s condition evaluated to a falsy boolean/string (see Truthiness -assert uses the same strict boolean-conversion rule as while, so a non-boolean/non-string condition raises E1011 instead of this).
assert(1 == 2, "one should equal two");
→ e.flx:1:36: error[E1027]: Assertion failed one should equal two
How to prevent it: This is usually intentional -it's how you assert a program invariant. If it's firing unexpectedly, the condition itself is the thing to debug, not the assert call.
Parser (syntax) errors
E2000 Syntax error (generic)
Reserved -not currently raised
Message shape: n/a
Reserved in the code enum, but has no registered definition and no raise-site at all in the current interpreter -even more dormant than the other reserved codes on this page, which at least have a registry entry.
How to prevent it: Not applicable.
E2001 Unexpected token
Fatal -halts execution
Message shape: varies, e.g. "Expected variable name.", "Expected ';' after expression"
The most general parser error -the parser expected one specific kind of token next (a name, a symbol, a keyword) and found something else. This is what you get for most everyday typos: a missing semicolon, a missing name, an empty tuple written as () instead of (,,), and so on.
let = 5;
→ e.flx:1:5: error[E2001]: Expected variable name.
How to prevent it: Read the message and the caret position carefully -it names exactly what token type was expected at that location.
E2002 Return outside function
Fatal -halts execution
Message shape: 'return' called outside a valid function
A return statement appeared outside any define(...) { ... } body -including at the top level of a script, or lexically inside a loop that isn't itself inside a function.
return 5;
→ e.flx:1:1: error[E2002]: 'return' called outside a valid function
How to prevent it: Only use return inside a function body.
E2003 Continue outside loop
Fatal -halts execution
Message shape: 'continue' called outside a valid loop
Same idea as E2004 below, for continue. Also correctly rejects a continue written inside a function that is itself nested inside a loop -the function boundary resets loop context.
continue;
→ e.flx:1:1: error[E2003]: 'continue' called outside a valid loop
How to prevent it: Only use continue directly inside a while or foreach body, not inside a function defined within one.
E2004 Break outside loop
Fatal -halts execution
Message shape: 'break' called outside a valid loop
A break appeared outside any loop -this includes a break inside a func that is itself nested inside a while/foreach; the check correctly treats the function boundary as resetting loop context, rather than (incorrectly) breaking the enclosing loop.
break;
→ e.flx:1:1: error[E2004]: 'break' called outside a valid loop
let i = 0;
while (i < 3) {
func inner = define() { break; }
inner();
}
→ e.flx:3:33: error[E2004]: 'break' called outside a valid loop -even though textually inside the while
How to prevent it: Only use break directly inside a while or foreach body.
E2005 Invalid assignment target
Fatal -halts execution
Message shape: the internal AST node name of the invalid target, e.g. "NumberLiteral"
The left-hand side of = (or a compound assignment) wasn't something assignable -a variable, a property, or an index. The message text is a little rough: it currently prints the parser's internal node type name rather than a friendly phrase.
5 = 10;
→ e.flx:1:3: error[E2005]: NumberLiteral
How to prevent it: Only assign to a plain variable name, a struct field/map key (x.y), or an index (x[0]) -never to a literal or the result of an expression.
E2006 Unexpected end of input
Fatal -halts execution
Message shape: the specific token that was still expected, e.g. "}"
The file ended while the parser was still expecting more -almost always a missing closing }, ), or ] somewhere earlier in the file.
func f = define() {
let x = 1;
→ e.flx:2:15: error[E2006]: }
How to prevent it: Count your braces/parens/brackets -an editor with bracket-matching helps a lot here, since Falx has no official syntax highlighting yet (see the roadmap).
E2007 Invalid default value creation
Fatal -halts execution
Message shape: Cannot have non default parameter <name> after default parameters.
A function parameter without a default appeared after one that has a default -see default parameters.
func f = define(a = 1, b) { return a; }
→ e.flx:1:24: error[E2007]: Cannot have non default parameter b after default parameters.
How to prevent it: Order parameters so every parameter with a default comes after every parameter without one.
Lexer errors
E3000 Invalid character
Fatal -halts execution
Message shape: Invalid character '<char>'.
The lexer found a character that isn't part of any valid Falx token -most often a stray symbol like @, $, or # that isn't one of Falx's operators.
let x = 5;
output(x @ 2);
→ e.flx:2:10: error[E3000]: Invalid character '@'.
How to prevent it: Remove the stray character -often the result of a typo, a copy-paste artifact, or an operator from another language that Falx doesn't support (like ** for exponentiation -see Operators).
E3001 Unterminated string
Fatal -halts execution
Message shape: Unterminated string.
A string literal opened with " but never found a matching closing quote before the end of the line/file.
let x = "hello;
→ e.flx:1:15: error[E3001]: Unterminated string.
How to prevent it: Add the missing closing ". If the string needs to contain a literal quote character, escape it as \" rather than ending the string early.
E3002 Invalid escape sequence
Fatal -halts execution
Message shape: Invalid escape character: <char>
A backslash inside a string was followed by a character that isn't one of Falx's supported escapes.
let x = "bad \q escape";
→ e.flx:1:15: error[E3002]: Invalid escape character: q
How to prevent it: Only use the supported escapes: \n, \t, \r, \f, \b, \", \\ -see Strings. For a literal backslash followed by another character, escape the backslash itself.
Module errors
E4000 Module not found
Fatal -halts execution
Message shape: Cannot find module '<name>' in path '<resolved path>'
require(...) couldn't resolve the given name to a real .flx file -including if the resolved path falls outside the project directory. See path resolution.
let x = require("doesnotexist");
→ e.flx:1:31: error[E4000]: Cannot find module 'doesnotexist' in path '/.../doesnotexist.flx'
How to prevent it: Check the spelling of the module name, and remember it's resolved relative to the requiring file (not the entry script) with .flx appended automatically -don't include the extension yourself.
E4001 Circular module dependency detected
Fatal -halts execution
Message shape: Circular module dependency detected: <chain>
Two or more modules require each other, directly or through a chain, so none of them could finish loading first.
// a.flx
let b = require("b");
// b.flx (in the same folder)
let a = require("a");
→ a.flx:2:20: error[E4001]: Circular module dependency detected: b -> a -> b (when running a.flx)
How to prevent it: Break the cycle -usually by moving the shared code both files need into a third module that neither of the original two depends on.
CLI errors
E5000 Missing filename
Reserved -not currently raised
Message shape: n/a
Registered for when run/--file/-f is used with no file argument. Verified unreachable: the actual CLI (built on Python's argparse) already rejects a missing file argument itself, with its own generic usage: ... message, before Falx's own diagnostic system ever gets a chance to raise this.
(running: python -m src.Main run -with no file argument)
→ usage: falx run [-h] file
falx run: error: the following arguments are required: file
How to prevent it: Always pass a file: python -m src.Main run script.flx.
E5001 Invalid file type
Reserved -not currently raised
Message shape: n/a
Registered for running a non-.flx file. Verified unreachable: the CLI does not currently check the file extension at all -a .txt file containing valid Falx source runs exactly the same as a .flx file.
# script.txt, containing:
output("works fine");
→ $ python -m src.Main run script.txt
works fine (no error, despite the extension)
How to prevent it: Not enforced today, but name your scripts .flx anyway for clarity and future-proofing -the check may be added later.