EK9 Compiler Error Index
This page provides detailed explanations for all EK9 compiler errors. Each error has a unique code (E01xxx-E50xxx) that helps you quickly find solutions.
Understanding EK9 Error Codes
EK9 error codes use the format E[PP][NNN] where:
- PP (01-20): Compilation phase number for phase-specific errors
- PP (50): Common errors that can occur in multiple phases
- NNN (001-999): Error number within that category
Examples
Error : E08020: 'result' on line 15: might be used before being initialised
See: https://ek9.io/errors.html#E08020
↑
Phase 08 (PRE_IR_CHECKS) - This error only occurs in phase 8
Error : E50001: 'symbol' on line 10: is not resolved
See: https://ek9.io/errors.html#E50001
↑
Common error - Can occur in phases 2, 3, or 4
Tip: Phase-specific errors (E01xxx-E20xxx) tell you exactly
which compilation phase detected the issue. Use -Xcp [PHASE_NAME]
to stop compilation at that phase for debugging.
Common Errors (E50xxx)
Some errors can occur in multiple compilation phases because they represent
fundamental issues that are checked throughout the compilation process.
These errors use the special prefix E50.
Example: E50001: NOT_RESOLVED can occur when:
- Phase 2 (SYMBOL_DEFINITION): Generic type parameter cannot be resolved
- Phase 3 (REFERENCE_CHECKS): Identifier lookup fails
- Phase 4 (EXPLICIT_TYPE_SYMBOL_DEFINITION): Parameterized type argument is not found
When you see an E50xxx error, check the full error message for context about which phase detected it and what type of symbol was being resolved.
Phase 01: PARSING
Parsing errors are detected during the initial source code parsing phase. These errors represent fundamental syntax and naming violations that prevent the compiler from understanding the structure of your code.
E01010: Invalid Symbol By Reference
Classification: INVALID_SYMBOL_BY_REFERENCE
Description
In the references section, you attempted to import a symbol using only its name
(e.g., SomeType). EK9 requires fully qualified references using the format
module.name::SymbolName (e.g., other.module::SomeType).
Writing just the symbol name without the module path causes this error because the compiler
cannot determine which module contains the symbol.
Example (Error)
#!ek9
defines module fuzztest.references.unqualified
references
@Error: REFERENCE_CHECKS: INVALID_SYMBOL_BY_REFERENCE
UnknownType
defines function
testFunc()
<- result as String: "test"
Solution
#!ek9
defines module example
references
other.module::SomeType
defines function
testFunc()
<- result as String: "test"
See Also
E01020: Invalid Module Name
Classification: INVALID_MODULE_NAME
Description
The module names org.ek9.lang and org.ek9.math are reserved
exclusively for EK9's built-in language features and standard library types (such as
String, Integer, List, etc.). Attempting to define
a module with either of these exact names causes this error. Module names follow dotted
notation (e.g., com.company.project) and can include most EK9 keywords as
segments (e.g., com.private.class.module is valid).
Example (Error)
#!ek9 @Error: PARSING: INVALID_MODULE_NAME defines module org.ek9.lang //Reserved namespace //EOF
Solution
#!ek9 defines module com.mycompany.utilities //Valid module name //EOF
Note: Only the specific names org.ek9.lang and
org.ek9.math are reserved. Other org.ek9.* module names
are allowed. Module segments can include EK9 keywords, making names like
company.private.function valid.
See Also
E01030: Duplicate Name
Classification: DUPLICATE_NAME
Description
A name has been used that conflicts with an existing function, creating ambiguity in the code. This error occurs when you attempt to use a function's name for a variable, parameter, property, method, or another construct. EK9 prevents such name collisions to ensure clarity about whether a name refers to a function or another symbol. This maintains unambiguous symbol resolution throughout your code.
Example (Error)
#!ek9
defines module bad.name.collisions1
defines class
C1
//This property collides with function of the same name
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: DUPLICATE_NAME
broken as String?
C1()
-> arg0 as String
this.broken: arg0
//This also collides with the function (that follows)
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: DUPLICATE_NAME
broken()
<- rtn <- true
defines function
broken()
var <- 3
assert var?
Solution
#!ek9
defines module example
defines function
processData()
<- rtn as Integer: 9
validFunction()
//Use a different parameter name - no collision
-> dataToProcess as String
<- rtn as Integer: 1
Common Causes
- Using a function name as a parameter or variable name
- Naming a class property or method the same as a module-level function
- Defining the same function multiple times
- Naming a service the same as an existing function
See Also
E01040: Duplicate Type
Classification: DUPLICATE_TYPE
Description
A type name has been used in a way that conflicts with an existing type. This error occurs when you define the same type (class, record, trait, etc.) multiple times, use a type name for a different construct (like a variable or function), or name a constructor the same as an existing type. Each type must have a unique name within its module scope to prevent ambiguity.
Example (Error)
#!ek9
defines module bad.duplicate.constructs
defines class
C1
someMethod()
-> arg0 as String
assert arg0?
@Error: SYMBOL_DEFINITION: DUPLICATE_TYPE
C1
someOtherMethod()
-> arg0 as String
assert arg0?
Solution
#!ek9
defines module example
defines class
Customer
name as String?
id as Integer?
Customer()
->
customerName as String
customerId as Integer
this.name: customerName
this.id: customerId
See Also
E01050: Possible Duplicate Enumerated Value
Classification: POSSIBLE_DUPLICATE_ENUMERATED_VALUE
Description
Enumeration values must be unique when normalized. EK9 detects duplicates by converting
all enumeration values to uppercase and removing underscores before checking for uniqueness.
This prevents exact duplicates (like Hearts appearing twice) as well as
similar values that could cause confusion (like ACTIVE and active,
or VALUE_1 and VALUE1). This ensures enumeration values are
clearly distinct and unambiguous.
Example (Error)
#!ek9
defines module bad.enumerations.check
//Deliberately add in Hearts twice
defines type
CardSuit as
Hearts
Diamonds
Clubs
Spades
@Error: SYMBOL_DEFINITION: POSSIBLE_DUPLICATE_ENUMERATED_VALUE
Hearts //Duplicate of Hearts above
Solution
#!ek9
defines module example
defines type
Status
ACTIVE
INACTIVE
PENDING //All distinct values
Note: EK9 normalizes enumeration values by converting to uppercase
and removing underscores. This means Status_OK, STATUS_OK,
and STATUSOK are all considered duplicates, as they normalize to the same value.
See Also
Phase 02: SYMBOL_DEFINITION
Symbol definition errors occur during the phase where the compiler builds its symbol table. These errors indicate duplicate symbols, conflicting names, or other issues with how types, methods, and properties are defined.
E02010: Duplicate Property Field
Classification: DUPLICATE_PROPERTY_FIELD
Description
A child class or record has declared a property with the same name as a property in its parent class or record. In EK9, you cannot redeclare or shadow properties from parent types. All properties in an inheritance hierarchy must have unique names. If you need different behavior, use a different property name in the child class.
Example (Error)
#!ek9
defines module bad.duplicateproperties.uses
defines record
RBase1 as open
prop1 <- String()
prop3 <- Date()
RExtension1 extends RBase1
@Error: FULL_RESOLUTION: DUPLICATE_PROPERTY_FIELD
prop3 <- String() //Duplicate: already defined in RBase1
Solution
#!ek9
defines module example
defines record
RBase1 as open
prop1 <- String()
prop3 <- Date()
RExtension1 extends RBase1
//Use a different property name - parent already has 'prop3'
extendedInfo <- String()
See Also
E02020: Cannot Support To JSON Duplicate Property Field
Classification: CANNOT_SUPPORT_TO_JSON_DUPLICATE_PROPERTY_FIELD
Description
A child class redeclares a property from its parent class and uses default operator,
which would auto-generate the $$ (to JSON) operator. The compiler cannot automatically
generate JSON serialization when property names collide because it creates ambiguity—which
prop1 should be serialized? You must either manually override operator $$
to handle the duplicate property explicitly, or use a different property name in the child class.
Example (Error)
#!ek9
defines module bad.defaulted.classoperators
defines class
CBase1 as open
prop1 <- String()
prop2 <- Date()
operator $$ as pure
<- rtn as JSON: JSON()
default operator
//Duplicate prop1 with default operator $$ causes JSON ambiguity
CExtension2 extends CBase1
@Error: FULL_RESOLUTION: CANNOT_SUPPORT_TO_JSON_DUPLICATE_PROPERTY_FIELD
prop1 <- Dimension()
default operator //Cannot generate $$ due to prop1 collision
Solution
Option 1: Manually override operator $$
#!ek9
defines class
CBase1 as open
prop1 <- String()
operator $$ as pure
<- rtn as JSON: JSON()
default operator
CExtension1 extends CBase1
prop1 <- Dimension() //OK because we manually handle $$
override operator $$ as pure
<- rtn as JSON: JSON() //Manually handle duplicate property
default operator //OK for other operators
Option 2: Use a different property name
#!ek9
defines class
CBase1 as open
prop1 <- String()
operator $$ as pure
<- rtn as JSON: JSON()
default operator
CExtension1 extends CBase1
extendedProp1 <- Dimension() //Different name avoids collision
default operator //Can auto-generate $$
See Also
E02030: Method Duplicated
Classification: METHOD_DUPLICATED
Description
Multiple constructors, methods, or operators with identical signatures have been defined
in the same type (class, record, trait, service, or component). A method signature consists
of its name and parameter types—access modifiers (public, private, protected) do NOT
differentiate signatures. This error also occurs when you define an explicit operator
implementation and also use default operator for the same operator, or when
you use default operator twice for the same operator. The compiler cannot
determine which implementation to use, creating ambiguity.
Example (Error)
#!ek9
defines module bad.duplicate.recordmethods
defines record
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: METHOD_DUPLICATED
R1
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: METHOD_DUPLICATED
R1() //First constructor
var <- "Steve"
assert var?
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: METHOD_DUPLICATED
R1() //Duplicate constructor with same signature
var <- "Steve"
assert var?
Solution
Option 1: Different method names
#!ek9
defines class
Calculator
add()
->
a as Integer
b as Integer
addWithBonus() //Different name
->
a as Integer
b as Integer
Option 2: Different parameter types (overloading)
#!ek9
defines class
Calculator
add()
->
a as Integer
b as Integer
add() //Different parameter types - valid overload
->
a as Float
b as Float
Option 3: For operators - remove duplicate or choose explicit vs default
#!ek9
defines record
R1
prop1 <- 0
//Choose ONE approach for each operator:
operator == as pure //Explicit implementation
-> arg0 as R1
<- rtn as Boolean: prop1 == arg0.prop1
//OR use default (not both):
//default operator ==
See Also
E02040: Duplicate Variable In Capture
Classification: DUPLICATE_VARIABLE_IN_CAPTURE
Description
When creating a dynamic class or function that captures variables from the enclosing scope,
duplicate variable names have been specified in the capture list. This error occurs in three
scenarios: (1) capturing the same identifier multiple times like (min, min),
(2) using the same target name multiple times like (threshold: min, threshold: max),
or (3) capturing different variables with the same internal name like (data: value1, data: value2).
All three scenarios create duplicate fields in the generated dynamic class, which would cause
ambiguity when accessing the captured values.
Example (Error)
#!ek9
defines module fuzz.dynamic.capture.same.variable.twice
defines function
discriminator() as abstract
-> s as String
<- rtn as Boolean?
defines program
TestCaptureSameVariableTwice()
min <- 10
//INVALID - capturing same identifier twice (unnamed)
@Error: SYMBOL_DEFINITION: DUPLICATE_VARIABLE_IN_CAPTURE
fn1 <- (min, min) is discriminator as function
rtn: length s > min
Example 2 (Error - Named Captures)
#!ek9
defines module fuzz.dynamic.capture.duplicate.names
defines trait
Validator
validate()
-> value as String
<- result as Boolean?
defines program
TestDuplicateCaptureNames()
min <- 10
max <- 100
//INVALID - duplicate name 'threshold' in capture
@Error: SYMBOL_DEFINITION: DUPLICATE_VARIABLE_IN_CAPTURE
validator <- (threshold: min, threshold: max) with trait of Validator as class
override validate()
-> value as String
<- result as Boolean: length value > threshold
assert validator?
Solution
Option 1: Capture each variable only once
#!ek9
defines function
discriminator() as abstract
-> s as String
<- rtn as Boolean?
defines program
Example()
min <- 9
max <- 40
gt <- (min) is discriminator as function
rtn: length s > min
lt <- (max) is discriminator
rtn: length s < max
Option 2: Use unique target names for named captures
#!ek9
defines trait
Validator
validate()
-> value as String
<- result as Boolean?
defines program
Example()
min <- 10
max <- 100
//Use different names: 'minThreshold' and 'maxThreshold'
validator <- (minThreshold: min, maxThreshold: max) with trait of Validator as class
override validate()
-> value as String
<- result as Boolean: length value > minThreshold and length value < maxThreshold
See Also
E02050: Duplicate Trait Reference
Classification: DUPLICATE_TRAIT_REFERENCE
Description
A trait, class, or component has directly referenced the same trait multiple times in its
with trait of declaration. Each trait should only be listed once in the declaration.
The compiler detects duplicates regardless of whether you use qualified names
(like module::Trait) or unqualified names. Note that it is perfectly valid for
the same trait to be included multiple times indirectly through the trait hierarchy (e.g.,
if trait T1 includes trait T2, and your class uses trait T1, getting T2 indirectly is fine).
This error only applies to direct, explicit duplication in the trait list.
Example (Error)
#!ek9
defines module bad.trait.use
defines trait
T1
method1()
<- rtn <- true
T2
method2()
<- rtn <- true
@Error: TYPE_HIERARCHY_CHECKS: DUPLICATE_TRAIT_REFERENCE
DuplicatedReferences1 with trait of T1, T2, T1 //T1 listed twice
method3()
<- rtn <- true
Solution
#!ek9
defines trait
Printable
print()
<- result as String?
defines trait
Saveable
save()
<- result as Boolean?
defines class
Document with trait of Printable, Saveable //Different traits
override print()
<- result as String: "Document"
override save()
<- result as Boolean: true
See Also
E02060: Duplicate Enumerated Values Present In Switch
Classification: DUPLICATE_ENUMERATED_VALUES_PRESENT_IN_SWITCH
Description
In a switch statement or expression on an enumeration type, the same enumerated value appears
in multiple case clauses. This error occurs whether the duplicate appears as
separate individual case values (like case Color.RED twice) or when a value
appears in a combined case and then again in a later case (like case Color.RED, Color.GREEN
followed by case Color.GREEN). Each enumerated value can appear at most once across
all case clauses to prevent unreachable code and ensure unambiguous behavior.
Example (Error)
#!ek9
defines module fuzztest.enumswitch.duplicate.single
defines type
Direction
NORTH,
SOUTH,
EAST,
WEST
defines program
testDuplicateEnumSingle()
dir <- Direction.NORTH
result as String?
switch dir
case Direction.NORTH
result: "Going North"
case Direction.SOUTH
result: "Going South"
case Direction.EAST
result: "Going East"
case Direction.WEST
result: "Going West"
@Error: FULL_RESOLUTION: DUPLICATE_ENUMERATED_VALUES_PRESENT_IN_SWITCH
case Direction.NORTH //Duplicate: NORTH already handled
result: "North again"
assert result?
Example 2 (Error - Combined Case)
#!ek9
defines module fuzztest.enumswitch.duplicate.combined
defines type
Color
RED,
GREEN,
BLUE,
YELLOW
defines program
testDuplicateEnumCombined()
color <- Color.RED
result as String?
switch color
case Color.RED, Color.GREEN //GREEN appears here
result: "Warm colors"
case Color.BLUE, Color.YELLOW
result: "Cool colors"
@Error: FULL_RESOLUTION: DUPLICATE_ENUMERATED_VALUES_PRESENT_IN_SWITCH
case Color.GREEN //Duplicate: GREEN already in combined case above
result: "Green again"
assert result?
Solution
#!ek9
defines type
Status
ACTIVE
INACTIVE
PENDING
defines function
processStatus()
-> status as Status
switch status
case ACTIVE
result <- "Active"
case INACTIVE //Different case value
result <- "Inactive"
default
result <- "Other"
See Also
- E01050: Possible Duplicate Enumerated Value
- E07310: Not All Enumerated Values Present In Switch
- Switch Statements
E02070: Service HTTP Path Duplicated
Classification: SERVICE_HTTP_PATH_DUPLICATED
Description
In a service definition, multiple HTTP endpoints have the same path structure with the same HTTP verb.
Path structure is determined by normalizing path variables to {} - meaning the actual
variable parameter names, types, and order do NOT matter for comparison. For example,
:/{userId}/data.html and :/{customerId}/data.html have the same structure
(:/{}/data.html) and would conflict if using the same HTTP verb (like GET). This duplicate
check is performed on a per-verb basis, so GET :/{id} and POST :/{id} do not
conflict. This prevents routing ambiguity - the system must know unambiguously which endpoint should
handle an incoming request.
Example (Error)
#!ek9
defines module fuzztest.service.path.duplicate
defines service
DuplicatePathService :/api
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: SERVICE_HTTP_PATH_DUPLICATED
getResource() as GET :/{id}
-> id as String
<- rtn as HTTPResponse: () with trait of HTTPResponse
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: SERVICE_HTTP_PATH_DUPLICATED
getAnotherResource() as GET :/{id} //Duplicate: same verb, same path
-> id as String
<- rtn as HTTPResponse: () with trait of HTTPResponse
Solution
#!ek9
defines service
UserService :/api
getResource() as GET :/{id}
-> id as String
<- rtn as HTTPResponse: () with trait of HTTPResponse
getResourceDetails() as GET :/{id}/details //Different path - OK
-> id as String
<- rtn as HTTPResponse: () with trait of HTTPResponse
Note: Different HTTP verbs (GET, POST, PUT, DELETE) on the same path are allowed and represent RESTful best practices. The error occurs when the same verb is used with the same path structure.
See Also
E02080: Delegate And Method Names Clash
Classification: DELEGATE_AND_METHOD_NAMES_CLASH
Description
A function delegate (either as a property/field or as a local variable) has the same name
as a method accessible in that scope. The method can be defined in the same class, in a parent
class, or in a trait that the class uses. When you call name(), this creates
ambiguity: does it invoke the function delegate or the method? EK9 detects this conflict
and requires you to use distinct names. Note that regular (non-delegate) variables can have
the same name as methods without error, though this is not recommended for code clarity.
Example (Error)
#!ek9
defines module bad.detailed.resolution
defines function
SomeFunction() as abstract
<- rtn as Integer?
defines class
C8A
method1()
<- rtn as Integer?
//This time also make a local delegate and then call it.
@Error: FULL_RESOLUTION: DELEGATE_AND_METHOD_NAMES_CLASH
method2 <- () is SomeFunction as function (rtn: 22)
//But it resolves the dynamic function above and NOT method below - hence the error as it is ambiguous
rtn: method2()
method2()
<- rtn <- false
Solution
#!ek9
defines class
Processor
processor as ProcessorFunction by default //Different name for delegate
process() //Method name is now unique
<- result as String: "Processing"
Best Practice: Use clear, distinct names for delegates and methods
to avoid confusion. Consider naming delegates with suffixes like Handler,
Function, or Delegate.
See Also
Phase 03: DUPLICATION_CHECK
Duplication check errors occur when the compiler validates that references and symbols don't conflict with each other across module boundaries and scopes.
E03010: Construct Reference Conflict
Classification: CONSTRUCT_REFERENCE_CONFLICT
Description
A locally defined construct (class, function, type, etc.) has the same name as a symbol
explicitly referenced in the references section. The reference declares your
intention to import and use a symbol from another module (or even your own module), but
then defining a local construct with the same name creates a contradiction: which one is
the "real" symbol to use? This includes self-references where you reference a symbol from
your own module and then define it locally.
Example (Error)
#!ek9
defines module construct.reference.conflict.test
//Import SharedClass and sharedFunction from external module
references
external.module.for.conflicts::SharedClass
external.module.for.conflicts::sharedFunction
//Define local class with same name - should trigger CONSTRUCT_REFERENCE_CONFLICT
defines class
SharedClass
default SharedClass()
//Define local function with same name - should trigger CONSTRUCT_REFERENCE_CONFLICT
defines function
sharedFunction()
<- rtn as String: "local"
Solutions
Option 1: Remove the local definition (use the referenced symbol)
#!ek9
defines module fuzztest.references.self
references
external.module::selfFunc //Use the external symbol
//Don't define selfFunc locally - just use the referenced one
defines program
TestProgram()
result <- selfFunc() //Calls external.module::selfFunc
assert result?
Option 2: Remove the reference (define it locally)
#!ek9
defines module fuzztest.references.self
//Don't reference it - define it locally instead
defines function
selfFunc() //Local definition - no conflict
<- result as String: "self"
Option 3: Rename the local construct
#!ek9
defines module my.module
references
external.module::SharedClass
defines class
LocalClass //Different name - no conflict
name as String?
See Also
E03020: References Conflict
Classification: REFERENCES_CONFLICT
Description
The same symbol (module::SymbolName) has been referenced multiple times
in the references section. This is redundant and creates an unnecessary
duplicate reference. Each symbol should only be referenced once. If you need to import
multiple symbols from the same module, list each distinct symbol once.
Example (Error)
#!ek9
defines module duplicate.references.test
//Reference the same class twice - redundant
references
external.module.for.conflicts::SharedClass
external.module.for.conflicts::SharedClass //Duplicate reference
defines function
testFunction()
<- rtn as String: "test"
Solution
#!ek9
defines module duplicate.references.test
//Reference each symbol only once
references
external.module.for.conflicts::SharedClass
defines function
testFunction()
<- rtn as String: "test"
See Also
E03030: Reference Does Not Resolve
Classification: REFERENCE_DOES_NOT_RESOLVED
Description
A symbol reference (module::Symbol) in the references section
cannot be resolved. This occurs when: (1) the referenced module doesn't exist, (2) the symbol
doesn't exist in that module (typo or wrong name), (3) case-sensitive mismatch (EK9 is
case-sensitive), or (4) attempting to reference built-in types that are automatically available
and cannot be explicitly referenced (like String, Integer, etc. from
org.ek9.lang). The compiler cannot find the specified symbol in the specified module.
Example 1 (Error - Nonexistent Module)
#!ek9
defines module fuzztest.references.missing.module
references
@Error: REFERENCE_CHECKS: REFERENCE_DOES_NOT_RESOLVED
com.nonexistent.module::SomeType //Module doesn't exist
defines function
testFunc()
<- result as String: "test"
Example 2 (Error - Nonexistent Symbol)
#!ek9
defines module fuzztest.references.nonexistent.symbol
references
@Error: REFERENCE_CHECKS: REFERENCE_DOES_NOT_RESOLVED
org.ek9lang.base::NonExistentType //Symbol doesn't exist in module
defines function
testFunc()
<- result as String: "test"
Common Causes
- Typo in the module name or symbol name
- Case-sensitive mismatch (e.g.,
TestFuncvstestFunc) - Attempting to reference built-in types (use them directly instead)
- Referenced module hasn't been compiled yet
- Module path configuration issue
Solutions
Option 1: Fix typos and case sensitivity
#!ek9
defines module my.module
references
external.module::SharedClass //Correct module and symbol name with proper case
Option 2: For built-in types, use them directly (no reference needed)
#!ek9
defines module my.module
//Don't reference built-in types - they're automatically available
defines function
test()
value <- String() //String is automatically available
assert value?
Option 3: Ensure module exists and is accessible
#!ek9
defines module my.module
references
company.utilities::Helper //Ensure company.utilities module exists and exports Helper
Build Order: Ensure that referenced modules are compiled before modules that depend on them. EK9's build system handles this automatically when modules are in the same project.
See Also
Phase 04: REFERENCE_CHECKS
Reference check errors validate that types are used correctly in specific contexts, ensuring type constraints, conversions, and template/generic requirements are met.
E04010: Type Cannot Be Constrained
Classification: TYPE_CANNOT_BE_CONSTRAINED
Description
When creating a type alias using defines type ... as, certain types cannot
be aliased or constrained. Specifically, functions, traits, abstract classes, and certain
built-in types (like Boolean and JSON) cannot be used as base
types for type definitions. EK9 restricts which types can be aliased to maintain type
system integrity and prevent confusion about type identity.
Example (Error)
#!ek9
defines module bad.constrainedtype.examples1
defines function
FunctionA()
<- rtn <- true
defines type
//Can't alias/constrain functions
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: TYPE_CANNOT_BE_CONSTRAINED
Other1 as FunctionA
//Can't alias/constrain Boolean
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: TYPE_CANNOT_BE_CONSTRAINED
Other2 as Boolean
//Can't alias/constrain JSON
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: TYPE_CANNOT_BE_CONSTRAINED
Other3 as JSON
Solution
#!ek9
defines module example
defines record
R1
default R1()
defines type
Index as Integer //OK - can alias Integer
Name as String //OK - can alias String
ValidOther as R1 //OK - can alias records
See Also
E04020: Type Must Be Convertible To String
Classification: TYPE_MUST_BE_CONVERTABLE_TO_STRING
Description
A type is being used in a string interpolation expression (like `Value is ${obj}`),
but the type doesn't provide a way to convert to String. EK9 requires types used in string
interpolation to implement either the $ (string) operator for direct string
conversion, or the #^ (promote to String) operator for type promotion. Without
one of these operators, the compiler cannot determine how to represent the object as a string.
Example (Error)
#!ek9
defines module bad.interpolated.strings
defines class
//Has no #^ to String and no $ string operator
C0
default C0() as pure
override operator ? as pure
<- rtn <- false
defines function
missingConversionToString1()
var <- C0()
@Error: FULL_RESOLUTION: TYPE_MUST_BE_CONVERTABLE_TO_STRING
aString <- `Value is ${var}`
assert aString?
Solution
#!ek9
defines module bad.interpolated.strings
defines class
C1
default C1() as pure
default operator $
defines function
correctExampleOfStringInterpolation1()
var <- C1()
aString <- `Value is ${var}`
assert aString?
See Also
E04030: Type Must Extend Exception
Classification: TYPE_MUST_EXTEND_EXCEPTION
Description
A type is being used in an exception-related context (such as throw
or catch) but doesn't extend the built-in Exception type.
EK9 enforces type safety in exception handling by requiring all thrown and caught objects
to inherit from Exception. This prevents throwing arbitrary types (like
String, Integer, or custom classes) which would make error
handling unpredictable and error-prone.
Example (Error)
#!ek9
defines module bad.trycatchfinally.example
defines function
-
Demonstrates trying to throw a class that is not an Exception or derives from Exception.
-?>
invalidExceptionFunction1()
<- rtn <- 22
if rtn == 22
@Error: FULL_RESOLUTION: TYPE_MUST_EXTEND_EXCEPTION
throw String("An Exception being thrown")
Solution
#!ek9
defines module bad.trycatchfinally.example
defines function
exceptionFunction()
<- rtn <- 22
if rtn == 22
throw Exception("An Exception being thrown")
See Also
E04040: Type Must Be Function
Classification: TYPE_MUST_BE_FUNCTION
Description
A type is being used in a context that requires a function or function delegate,
but the type is not a function type. This commonly occurs with higher-order
functions or stream pipeline operations like call or async.
Example (Error)
#!ek9
defines module bad.streams5
defines class
StringCollector
joined <- String()
operator |
-> arg0 as String
if arg0?
if joined?
joined += " " + arg0
else
joined: String(arg0)
override operator ? as pure
<- rtn as Boolean: joined?
defines function
-
So this checks for the type being streamed through is actually a function.
In this error case it is just an Integer so cannot be 'called'.
-?>
BrokenStreamCatCall2()
collector <- StringCollector()
@Error: FULL_RESOLUTION: TYPE_MUST_BE_FUNCTION
cat [1, 2] | call > collector
assert collector?
Solution
#!ek9
defines module bad.streams5
defines function
abstractFunction() as abstract
<- rtn as String?
getSteve() is abstractFunction
<- rtn <- "Steve"
getLimb() is abstractFunction
<- rtn <- "Limb"
-
Example of a List of functions being streamed through a pipeline and called.
-?>
SimpleStreamCatCall1()
collector <- StringCollector()
cat [getSteve, getLimb] | call > collector
assert collector?
See Also
E04050: Type Must Be Simple
Classification: TYPE_MUST_BE_SIMPLE
Description
When using type inference with the <- declaration operator, the type being
inferred must be "simple" - either a literal value or a simple constructor call. Complex
expressions involving method calls, operators, or function invocations cannot be used for
type inference. This ensures the compiler can reliably determine types early in compilation
before all methods and operators are fully resolved.
Example (Error)
#!ek9
defines module bad.inferred.returns
defines function
-
This however is a complex expression.
-?>
invalidExpressionUse1()
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: TYPE_MUST_BE_SIMPLE
<- aBitComplex <- List("Steve").get(0)
Solution
#!ek9
defines module bad.inferred.returns
defines function
validSimpleReturn()
<- rtn <- Integer()
validListOfStringReturn()
<- rtn <- ["Steve", "Stephen", "Steven"]
validDictOfIntegerStringReturn()
<- rtn <- {1: "Steve", 2: "Stephen", 3: "Steven"}
See Also
- Variable Declarations
- Type Inference
E04060: Is Not An Aggregate Type
Classification: IS_NOT_AN_AGGREGATE_TYPE
Description
A type is being used in a stream pipeline operation (like sort, group,
or uniq) where an aggregate type (class, record, component) is required, but the
type provided is not an aggregate. Stream operations like sorting and grouping need structured
data types with properties and methods (such as comparison operators) to function correctly.
Functions, function delegates, and built-in simple types (like Integer alone) don't
provide this structure. You must stream aggregate types that can be compared, grouped, or otherwise
manipulated by the pipeline operation.
Example (Error)
#!ek9
defines module bad.streams6
defines class
StringCollector
joined <- String()
operator |
-> arg0 as String
if arg0?
if joined?
joined += " " + arg0
else
joined: String(arg0)
override operator ? as pure
<- rtn as Boolean: joined?
defines function
ExamplePipeLineFunction()
<- rtn <- true
-
This time a function is the pipeline type and not some 'aggregate', so this cannot be used to sort.
-?>
InvalidComparatorFunctionStreamCatSort6()
collector <- StringCollector()
@Error: FULL_RESOLUTION: IS_NOT_AN_AGGREGATE_TYPE
cat [ExamplePipeLineFunction] | sort > collector
assert collector?
Solution
#!ek9
defines module bad.streams6
defines record
R2
prop1 as String: String()
prop2 as Date: Date()
R2()
->
p1 as String
p2 as Date
this.prop1 :=: p1
this.prop2 :=: p2
operator <=> as pure
-> o as R2
<- rtn as Integer?
prop1Result <- prop1 <=> o.prop1
rtn :=? ~prop1Result? or prop1Result == 0 <- prop2 <=> o.prop2 else prop1Result
default operator $
defines function
ComparatorStreamCatSort2()
collector <- StringCollector()
cat [R2("last", 2010-10-01), R2("last", 2010-10-02), R2("first", 2010-10-01)] | sort > collector
assert collector?
See Also
E04070: Not A Template
Classification: NOT_A_TEMPLATE
Description
Type parameters are being provided to a type or function that is not generic/template.
Only types and functions explicitly defined as generic can accept type parameters.
This error occurs when attempting to parameterize built-in types like Date,
String, or user-defined non-generic types.
Example (Error)
#!ek9
defines module bad.use.non.generic
defines function
aNonGenericFunction()
-> arg1 as String
<- rtn as String: arg1
badClassUseOfNonGeneric()
//Failure 1
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: NOT_A_TEMPLATE
notActuallyGeneric <- Date() of String
//Failure 2
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: NOT_A_TEMPLATE
alsoNotGeneric as Date of String?
Solution
#!ek9
defines module bad.use.non.generic
defines function
validClassUse()
//Use Date without type parameters
validDate <- Date()
//Use generic List with type parameters
validList <- List() of String
assert validList?
Or define a generic type:
#!ek9
defines class
GenericContainer of type T //Generic type
value as T?
default GenericContainer()
GenericContainer()
-> val as T
value: val
defines function
demo()
container <- GenericContainer() of String //OK - generic type
See Also
E04080: Template Type Requires Parameterization
Classification: TEMPLATE_TYPE_REQUIRES_PARAMETERIZATION
Description
A generic/template type is being used without providing the required type parameters.
When a type is defined as generic (e.g., of type T), it must be
parameterized with concrete types when used in declarations.
Example (Error)
#!ek9
defines module bad.use.conceptual.parameters
defines class
C1 of type T
prop1 as T?
default C1()
C1()
-> arg T
prop1: arg
defines function
InvalidParameterization()
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: TEMPLATE_TYPE_REQUIRES_PARAMETERIZATION
-> arg as C1
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: TEMPLATE_TYPE_REQUIRES_PARAMETERIZATION
<- rtn as C1: arg
assert arg?
Solution
#!ek9
defines module bad.use.conceptual.parameters
defines class
C1 of type T
prop1 as T?
default C1()
C1()
-> arg T
prop1: arg
defines function
ValidParameterization()
-> arg as C1 of String
<- rtn as C1 of String: arg
assert arg?
See Also
Phase 05: EXPLICIT_TYPE_SYMBOL_DEFINITION
Type symbol definition errors validate inheritance hierarchies, method overriding, and proper use of type system features like this/super, constructors, and purity.
E05020: Circular Hierarchy Detected
Classification: CIRCULAR_HIERARCHY_DETECTED
Description
A circular type or function hierarchy has been detected. This occurs when type A extends type B, which extends type C, which extends back to type A (forming a cycle). Circular inheritance creates an impossible and infinite inheritance chain that cannot be resolved. EK9 detects both direct circular references (A extends B, B extends A) and indirect ones through multiple levels (A → B → C → A). This applies to classes, records, traits, functions, and components.
Example (Error)
#!ek9
defines module bad.classes.hierarchies
defines class
//3-way circular: C1 → C3 → C2 → C1
@Error: TYPE_HIERARCHY_CHECKS: CIRCULAR_HIERARCHY_DETECTED
C1 is C3 as open
field1 <- "Steve"
default C1()
C2 is C1 as open
field2 <- "Stephen"
default C2()
C3 is C2 as open
field3 <- "Stephene"
default C3()
Example 2 (Error - Direct Circular)
#!ek9
defines module bad.classes.hierarchies
defines class
//2-way circular: CA → CB → CA
@Error: TYPE_HIERARCHY_CHECKS: CIRCULAR_HIERARCHY_DETECTED
CA is CB as open
field3 <- "Stephene"
default CA()
CB is CA as open
field4 <- "Steven"
default CB()
Solution
#!ek9
defines class
BaseClass
value as String?
defines class
ClassA extends BaseClass
data as Integer?
defines class
ClassB extends BaseClass //Linear hierarchy - no cycles
name as String?
See Also
E05030: Not Open To Extension
Classification: NOT_OPEN_TO_EXTENSION
Description
A type (class, record, function, or trait) is not marked as open (or implicitly
open via abstract) but is being extended. By default, EK9 types are closed for
extension to encourage composition over inheritance and prevent fragile base class problems.
To allow a type to be extended, explicitly mark it as open or as abstract
(abstract types are implicitly open). This design encourages intentional inheritance hierarchies
rather than accidental coupling.
Example (Error)
#!ek9
defines module bad.inherited.classes
defines class
//Explicitly open - can be extended
Class1 as open
firstName <- "Steve"
lastName <- "Limb"
//Abstract classes are implicitly open - can be extended
Class2 is Class1 as abstract
dob <- Date()
//Can extend Class2 because it's abstract (implicitly open)
Class3 extends Class2
postCode <- String()
//ERROR: Class3 is NOT marked as open - cannot be extended
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: NOT_OPEN_TO_EXTENSION
Class4 is Class3
country <- String()
Solution
#!ek9
defines module example
defines class
Class1 as open
firstName <- "Steve"
Class2 is Class1 as abstract
dob <- Date()
//Mark Class3 as open to allow further extension
Class3 extends Class2 as open
postCode <- String()
//Now Class4 can extend Class3
Class4 is Class3
country <- String()
Design Principle: EK9 favors composition over inheritance. Only mark
types as open when inheritance is genuinely the best design choice. Abstract
types are implicitly open because they require concrete implementations.
See Also
E05040: Super For Any Not Required
Classification: SUPER_FOR_ANY_NOT_REQUIRED
Description
Calling super when there is no explicit superclass (only the implicit
Any base class) is not required and produces an error. All EK9 types
automatically and implicitly extend Any, but Any is special—it
has no methods to override and no constructor to call. Calling super.method()
or super() when your class has no explicit superclass is meaningless because
there's no parent implementation to invoke. Only use super when you have an
explicit superclass (via extends or is).
Example (Error)
#!ek9
defines module bad.classes.thisandsuper
defines class
InvalidClass1
method1()
//This is not valid because there is no explicit super (only implicit Any).
@Error: FULL_RESOLUTION: SUPER_FOR_ANY_NOT_REQUIRED
super.method1()
Solution
#!ek9
defines class
MyClass
default MyClass()
//No super() call needed for implicit Any base
value := "initialized"
See Also
E05050: This And Super Must Be First In Constructor
Classification: THIS_AND_SUPER_MUST_BE_FIRST_IN_CONSTRUCTOR
Description
When calling this() or super() as constructor delegation in a
constructor, the call must be the first statement. This ensures proper initialization order
and prevents accessing uninitialized state or executing logic before the object is properly
constructed. Once you've called super() or this() once, you cannot
call them again in the same constructor. This rule applies even when the calls appear in
nested blocks within the constructor.
Example (Error)
#!ek9
defines module bad.pure.scenarios2
defines class
C0 as open
C0() as pure
var1 <- true
assert var1?
C1 extends C0
prop1 <- String()
C1() as pure
-> arg0 as C1
//Some comment, but we can call super() here
super(arg0)
//ERROR: Cannot call super() again - must be first statement only
@Error: FULL_RESOLUTION: THIS_AND_SUPER_MUST_BE_FIRST_IN_CONSTRUCTOR
super(arg0)
//ERROR: Cannot call this() after super()
@Error: FULL_RESOLUTION: THIS_AND_SUPER_MUST_BE_FIRST_IN_CONSTRUCTOR
this()
Solution
#!ek9
defines class
C0 as open
C0() as pure
var1 <- true
assert var1?
C1 extends C0
prop1 <- String()
C1() as pure
-> arg0 as C1
super(arg0) //First statement - call super once
//Additional initialization after super()
this.prop1 :=? "initialized"
See Also
E05060: This And Super Calls Only In Constructor
Classification: THIS_AND_SUPER_CALLS_ONLY_IN_CONSTRUCTOR
Description
Constructor delegation using this() or super() can only
be used within constructors. These special calls invoke another constructor and are
not regular methods—they cannot be called from regular methods, operators, or functions.
If you need to reuse initialization logic, factor it out into a regular method that
both constructors and other methods can call.
Example (Error)
#!ek9
defines module bad.pure.scenarios2
defines class
C0 as open
propA <- Integer()
C0() as pure
var1 <- true
assert var1?
badMethod2()
<- rtn <- false
//ERROR: Cannot call constructor from regular method
@Error: FULL_RESOLUTION: THIS_AND_SUPER_CALLS_ONLY_IN_CONSTRUCTOR
aCheck <- this().checkMethod()
checkMethod()
<- rtn <- true
Solution
#!ek9
defines class
C0 as open
propA <- Integer()
C0() as pure
var1 <- true
commonInitialization()
C0() as pure
-> arg0 as C0
this() //OK - constructor delegation in constructor
this :=: arg0
//Factor out common logic into a regular method
commonInitialization()
propA: 0
badMethod2()
<- rtn <- false
commonInitialization() //Call regular method instead
rtn: true
See Also
E05070: Inappropriate Use Of This
Classification: INAPPROPRIATE_USE_OF_THIS
Description
The this keyword is being used in an inappropriate context. While this
can be used as a value (to reference the current object), as a property accessor
(this.property), or with specific operators like :=: (copy),
:~: (merge), :^: (replace), and mutation operators (+=,
etc.), it cannot be used in certain ways. Specifically, this() as a function call
is only valid in constructors for constructor delegation. Attempting to call this()
in a regular method, function, or other non-constructor context is inappropriate.
Example (Error)
#!ek9
defines module bad.pure.scenarios2
defines function
CheckWhyThisCanBeUsed() as pure
-> arg0 as Boolean
//You can use 'this' as a value in functions
someHolderMaybeToBePassedAbout <- this
assert someHolderMaybeToBePassedAbout?
//ERROR: Cannot call this() in a function - only in constructors
@Error: FULL_RESOLUTION: INAPPROPRIATE_USE_OF_THIS
this()
Solution
#!ek9
defines function
CheckWhyThisCanBeUsed() as pure
-> arg0 as Boolean
//OK: Use this as a value (function delegate)
someHolder <- this
assert someHolder?
//OK: Call the delegate with proper arguments
someHolder(true)
See Also
E05080: Inappropriate Use Of Super
Classification: INAPPROPRIATE_USE_OF_SUPER
Description
The super keyword is being used inappropriately. While super can be
used with specific operators (like :=:, :~:, :^:)
to mutate the superclass portion of an object, it cannot be:
- Assigned to variables (neither declaration
<-nor assignment:=) - Passed as a parameter to methods or functions
- Used as a standalone value
The super keyword refers to the superclass portion of the current object and is
only meaningful for accessing inherited properties/methods or applying mutation operators.
Example (Error)
#!ek9
defines module bad.pure.scenarios2
defines class
C0 as open
propA <- Integer()
default C0()
C1 extends C0
prop1 <- String()
badMethod1()
<- rtn <- false
//ERROR: Cannot declare a variable with super as value
@Error: FULL_RESOLUTION: INAPPROPRIATE_USE_OF_SUPER
theSuper <- super
//ERROR: Cannot assign super to a variable
@Error: FULL_RESOLUTION: INAPPROPRIATE_USE_OF_SUPER
theSuper := super
//ERROR: Cannot pass super as a parameter
@Error: FULL_RESOLUTION: INAPPROPRIATE_USE_OF_SUPER
this.callMethod(super)
callMethod()
-> arg0 as C0
assert arg0?
Solution
#!ek9
defines module bad.pure.scenarios2
defines class
C0 as open
propA <- Integer()
default C0()
C1 extends C0
prop1 <- String()
goodMethod1()
<- rtn <- false
//VALID: Use 'this' instead (C1 is a C0)
theThis <- this
theThis := this
this.callMethod(this)
//VALID: Apply mutation operators to super
super :=: this
super :~: this
callMethod()
-> arg0 as C0
assert arg0?
See Also
E05090: Use Of This Or Super Inappropriate
Classification: USE_OF_THIS_OR_SUPER_INAPPROPRIATE
Description
This error occurs when this or super is used with operators that are
semantically invalid for object self-reference. EK9 allows mutation of this and
super using specific operators, but disallows operations that would be confusing
or nonsensical:
- Initialization operator (
:) - Cannot use withthis(should use:=:copy operator) - Direct assignment (
:=) - Cannot reassignthisorsuper(use mutation operators) - Guarded assignment (
:=?) - Cannot use withthis(thisis always set)
Valid operators for this/super:
:=: (copy), :~: (merge), :^: (replace),
+=, -=, *=, /=.
Example (Error)
#!ek9
defines module bad.classassignment.use
defines class
C1
p1 as String: String()
//ERROR: Cannot use initialization operator with this
C1()
-> param as C1
@Error: SYMBOL_DEFINITION: USE_OF_THIS_OR_SUPER_INAPPROPRIATE
this: param
C2
p1 as String: String()
//ERROR: Guarded assignment makes no sense - this is always set
C2()
-> param as C2
@Error: SYMBOL_DEFINITION: USE_OF_THIS_OR_SUPER_INAPPROPRIATE
this :=? param
C2a
p1 as String: String()
//ERROR: Cannot use direct assignment := to this
C2a()
-> param as C2a
@Error: SYMBOL_DEFINITION: USE_OF_THIS_OR_SUPER_INAPPROPRIATE
this := param
C2b extends C2a
p2 as String: String()
//ERROR: Cannot use direct assignment := to super
C2b()
-> param as C2a
@Error: SYMBOL_DEFINITION: USE_OF_THIS_OR_SUPER_INAPPROPRIATE
super := param
Solution (Valid Mutation Operators)
#!ek9
defines module bad.classassignment.use
defines class
C3
p1 as String: String()
//VALID: Copy operator - copies values from param into this
C3()
-> param as C3
this :=: param
operator :=:
-> param as C3
this.p1 :=: param.p1
C4
p1 as String: String()
p2 as String: "Steve"
//VALID: Merge operator - merges values from param into this
C4()
-> param as C4
this :~: param
operator :~:
-> param as C4
this.p1 :~: param.p1
this.p2 :~: param.p2
See Also
E05100: Override Inappropriate
Classification: OVERRIDE_INAPPROPRIATE
Description
The override modifier is used in a context where it cannot be applied.
This error occurs when override is used with constructs that don't support
inheritance or method overriding:
- Programs - Cannot extend other programs, so override is meaningless
- Services - Cannot extend other services, so override is meaningless
- Records - Cannot have methods beyond constructors/operators, so override is not applicable
- Functions - Standalone constructs that cannot be overridden
Note: If you're trying to use override in a class/trait context
where the method doesn't actually override anything, see E05110: Does Not Override.
Example (Error)
#!ek9
defines module bad.programs.examples
defines program
//Valid program without override
Program1()
someValue <- 1
assert someValue?
//ERROR: Programs cannot extend, so override is inappropriate
@Error: SYMBOL_DEFINITION: OVERRIDE_INAPPROPRIATE
override Program2()
someValue <- 1
assert someValue?
defines module bad.services.use
defines service
Addresses :/addresses/something
//Valid service method
byId() as GET for :/{address-id}
-> addressId as AddressId :=: PATH "address-id"
<- response as HTTPResponse: HTTPResponse()
//ERROR: Services cannot extend, so override is inappropriate
@Error: SYMBOL_DEFINITION: OVERRIDE_INAPPROPRIATE
override anotherInvalidMethod()
<- incomingContent as String: "Steve"
Solution
#!ek9
defines module bad.programs.examples
defines program
//CORRECT: Remove override modifier
Program2()
someValue <- 1
assert someValue?
defines module bad.services.use
defines service
Addresses :/addresses/something
//CORRECT: Remove override modifier
anotherValidMethod()
<- incomingContent as String: "Steve"
See Also
E05110: Does Not Override
Classification: DOES_NOT_OVERRIDE
Description
A method is marked with override but there is no matching method in the
superclass or traits to actually override. This typically occurs when:
- The superclass method has a different signature (name, parameters, or return type)
- The superclass method is private (private methods are hidden and cannot be overridden)
- There is no such method in the superclass at all
Key principle: Private methods in a superclass are hidden from subclasses. A method with the same signature in a subclass is a new method, not an override.
Example (Error)
#!ek9
defines module bad.overriding.classmethods1
defines class
C1 as open
//Private method - hidden from subclasses
private someMethod()
-> arg0 as String
<- rtn as String: arg0
//ERROR: Cannot override private method - it's hidden
C3u extends C1
@Error: FULL_RESOLUTION: DOES_NOT_OVERRIDE
override someMethod()
-> arg0 as String
<- rtn as String: `[ ${arg0} ]`
//ERROR: Cannot override private method even with protected
C4u extends C1
@Error: FULL_RESOLUTION: DOES_NOT_OVERRIDE
override protected someMethod()
-> arg0 as String
<- rtn as String: `[ ${arg0} ]`
Solutions
Option 1: Remove override modifier (new method)
#!ek9
defines module bad.overriding.classmethods1
defines class
C1 as open
private someMethod()
-> arg0 as String
<- rtn as String: arg0
//CORRECT: This is a NEW method (not overriding private method)
C3 extends C1
someMethod()
-> arg0 as String
<- rtn as String: `[ ${arg0} ]`
Option 2: Make superclass method non-private
#!ek9
defines module bad.overriding.classmethods1
defines class
C5 as open
//Protected or public - can be overridden
protected someMethod()
-> arg0 as String
<- rtn as String: arg0
//CORRECT: Now we ARE overriding
OkOverrides1 extends C5
override protected someMethod()
-> arg0 as String
<- rtn as String: arg0
Design Principle: EK9 requires that override only be used
when actually overriding a visible (non-private) method from a superclass or trait.
See Also
E05120: Method Overrides
Classification: METHOD_OVERRIDES
Description
A method in a subclass has the same signature as a method in the superclass (or trait),
which means it is overriding the parent method, but the override
keyword is missing. EK9 requires explicit declaration of overrides to prevent accidental
method shadowing and make inheritance relationships clear.
Key difference from E05110:
- METHOD_OVERRIDES (E05120): You ARE overriding but forgot to say
override - DOES_NOT_OVERRIDE (E05110): You said
overridebut there's nothing to override
Example (Error)
#!ek9
defines module bad.overriding.classmethods1
defines class
C5 as open
//Protected method that CAN be overridden
protected someMethod()
-> arg0 as String
<- rtn as String: arg0
//ERROR: This IS overriding but missing 'override' keyword
NeedsToExpressOverrides1 extends C5 as open
@Error: FULL_RESOLUTION: METHOD_OVERRIDES
protected someMethod()
-> arg0 as String
<- rtn as String: arg0
C8 as open
//Public method that CAN be overridden
someMethod()
-> arg0 as String
<- rtn as String: arg0
//ERROR: This IS overriding but missing 'override' keyword
NeedsToExpressOverrides2 extends C8
@Error: FULL_RESOLUTION: METHOD_OVERRIDES
someMethod()
-> arg0 as String
<- rtn as String: arg0
Solution
#!ek9
defines module bad.overriding.classmethods1
defines class
C5 as open
protected someMethod()
-> arg0 as String
<- rtn as String: arg0
//CORRECT: Explicit 'override' keyword
OkOverrides1 extends C5
override protected someMethod()
-> arg0 as String
<- rtn as String: arg0
C8 as open
someMethod()
-> arg0 as String
<- rtn as String: arg0
//CORRECT: Explicit 'override' keyword
OkOverrides2 extends C8
override someMethod()
-> arg0 as String
<- rtn as String: arg0
Design Principle: Explicit override prevents accidental method
shadowing. If you have the same signature as a superclass method, EK9 assumes you intend
to override it and requires you to be explicit about that intention.
See Also
E05130: Method Access Modifiers Differ
Classification: METHOD_ACCESS_MODIFIERS_DIFFER
Description
A method in a subclass has the same signature as a method in the superclass but uses a different access modifier. EK9 enforces the Liskov Substitution Principle: overriding methods cannot change visibility in any way.
EK9 Access Modifiers (least to most visible):
private- Only visible within the classprotected- Visible within the class and subclasses(default/public)- Visible everywhere
Key rule: You cannot change the access modifier when overriding. EK9 is stricter than Java - even expanding visibility is disallowed.
Example (Error)
#!ek9
defines module bad.overriding.classmethods1
defines class
C5 as open
//Protected method
protected someMethod()
-> arg0 as String
<- rtn as String: arg0
//ERROR: Cannot restrict protected to private
C6 extends C5
@Error: FULL_RESOLUTION: METHOD_ACCESS_MODIFIERS_DIFFER
private someMethod()
-> arg0 as String
<- rtn as String: `[ ${arg0} ]`
//ERROR: Cannot expand protected to public
C7 extends C5
@Error: FULL_RESOLUTION: METHOD_ACCESS_MODIFIERS_DIFFER
someMethod()
-> arg0 as String
<- rtn as String: `[ ${arg0} ]`
C8 as open
//Public method (no modifier = public)
someMethod()
-> arg0 as String
<- rtn as String: arg0
//ERROR: Cannot restrict public to private
C9 extends C8
@Error: FULL_RESOLUTION: METHOD_ACCESS_MODIFIERS_DIFFER
private someMethod()
-> arg0 as String
<- rtn as String: `[ ${arg0} ]`
//ERROR: Cannot restrict public to protected
C10 extends C8 as open
@Error: FULL_RESOLUTION: METHOD_ACCESS_MODIFIERS_DIFFER
protected someMethod()
-> arg0 as String
<- rtn as String: `[ ${arg0} ]`
Solution
#!ek9
defines module bad.overriding.classmethods1
defines class
C5 as open
protected someMethod()
-> arg0 as String
<- rtn as String: arg0
//CORRECT: Keep same access modifier (protected)
OkOverrides1 extends C5
override protected someMethod()
-> arg0 as String
<- rtn as String: arg0
C8 as open
someMethod()
-> arg0 as String
<- rtn as String: arg0
//CORRECT: Keep same access modifier (public)
OkOverrides2 extends C8
override someMethod()
-> arg0 as String
<- rtn as String: arg0
Liskov Substitution Principle: Derived classes must be substitutable for their base classes. Changing access modifiers violates this principle by altering the contract of the method.
See Also
E05140: Function Signature Does Not Match Super
Classification: FUNCTION_SIGNATURE_DOES_NOT_MATCH_SUPER
Description
A function that uses is to extend another function has a signature that doesn't
match the super function. When extending functions, the signature must be identical:
- Parameter types must match exactly
- Parameter count must match exactly
- Return type must match exactly
Note: EK9 functions use is keyword for inheritance, not extends.
Example (Error)
#!ek9
defines module bad.overriding.functions
defines function
//Base function expecting String parameter
Function1() as open
-> arg0 as String
<- rtn <- true
//Abstract function expecting String parameter
AbstractFunction1() as abstract
-> arg0 as String
<- rtn as Boolean?
//ERROR: Parameter type mismatch (Integer vs String)
@Error: FULL_RESOLUTION: FUNCTION_SIGNATURE_DOES_NOT_MATCH_SUPER
InvalidFunction1() is Function1
-> arg0 as Integer
<- rtn <- true
//ERROR: Parameter type mismatch (Float vs String)
@Error: FULL_RESOLUTION: FUNCTION_SIGNATURE_DOES_NOT_MATCH_SUPER
InvalidFunction2() is AbstractFunction1
-> arg0 as Float
<- rtn <- true
Solution
#!ek9
defines module bad.overriding.functions
defines function
Function1() as open
-> arg0 as String
<- rtn <- true
AbstractFunction1() as abstract
-> arg0 as String
<- rtn as Boolean?
//CORRECT: Exact signature match
ValidFunction1() is Function1
-> arg0 as String
<- rtn as Boolean: arg0?
//CORRECT: Exact signature match (can remain abstract)
ValidFunction2() is AbstractFunction1 as abstract
-> arg0 as String
<- rtn as Boolean?
See Also
E05150: Super Is Pure
Classification: SUPER_IS_PURE
Description
A function/method extends a pure super function/method but is not itself
marked as pure. Purity is a guarantee in EK9: once something
is declared pure, all implementations must maintain that guarantee.
Why this matters: If a caller receives a reference typed as the super (pure) function/method, they rely on the pure guarantee. Allowing impure implementations would break that contract and violate the Liskov Substitution Principle.
Example (Error)
#!ek9
defines module bad.overriding.functions
defines function
//Super function is PURE
Function2() as pure open
-> arg0 as String
<- rtn <- true
//ERROR: Extending pure function without being pure
@Error: FULL_RESOLUTION: SUPER_IS_PURE
InvalidFunction5() is Function2
-> arg0 as String
<- rtn <- true
Solution
#!ek9
defines module bad.overriding.functions
defines function
Function2() as pure open
-> arg0 as String
<- rtn <- true
//CORRECT: Maintain purity guarantee
ValidFunction() is Function2 as pure
-> arg0 as String
<- rtn <- true
See Also
E05160: Super Is Not Pure
Classification: SUPER_IS_NOT_PURE
Description
A function/method tries to be pure while extending a super function/method
that is not pure. You cannot add purity constraints when the parent doesn't
have them - this would violate the Liskov Substitution Principle.
Why this matters: The super function's contract doesn't guarantee purity. Code that depends on the super type has no expectation of pure behavior. Attempting to add purity in a subtype would create an incompatible contract.
Example (Error)
#!ek9
defines module bad.overriding.functions
defines function
//Super function is NOT pure
Function1() as open
-> arg0 as String
<- rtn <- true
//ERROR: Cannot add purity when super is not pure
@Error: FULL_RESOLUTION: SUPER_IS_NOT_PURE
InvalidFunction6() is Function1 as pure
-> arg0 as String
<- rtn <- true
Solution
#!ek9
defines module bad.overriding.functions
defines function
Function1() as open
-> arg0 as String
<- rtn <- true
//CORRECT: Match parent's purity (not pure)
ValidFunction() is Function1
-> arg0 as String
<- rtn <- true
See Also
E05170: Dispatcher Pure Mismatch
Classification: DISPATCHER_PURE_MISMATCH
Description
A dispatcher method is marked as pure, but one or more of its dispatch target
methods (methods with matching name but more specific parameter types) are not
marked as pure. All dispatch targets must match the purity of the dispatcher
entry point.
Why this matters: When code calls the dispatcher with the general parameter type, it expects pure behavior. The dispatcher might route to any of the specific implementations. If those implementations aren't pure, the purity guarantee is violated.
Example (Error)
#!ek9
defines module bad.dispatchermethods
defines class
BadDispatcher4
//Dispatcher entry point is PURE
process() as pure dispatcher
-> arg as Any
assert arg?
//ERROR: Dispatch target is NOT pure
@Error: FULL_RESOLUTION: DISPATCHER_PURE_MISMATCH
process()
-> arg as Integer
assert arg?
Solution
#!ek9
defines module bad.dispatchermethods
defines class
GoodDispatcher
//Dispatcher entry point is pure
process() as pure dispatcher
-> arg as Any
assert arg?
//CORRECT: All dispatch targets must also be pure
process() as pure
-> arg as Integer
assert arg?
process() as pure
-> arg as String
assert arg?
See Also
E05180: Dispatcher Private In Super
Classification: DISPATCHER_PRIVATE_IN_SUPER
Description
A class defines a dispatcher method, and the superclass has a private method
with a matching signature. This is problematic because:
- Private methods in the superclass are hidden from subclasses
- The dispatcher cannot dispatch to private methods
- This likely indicates a design error (the super method should probably be protected/public)
Why this matters: Dispatchers rely on method visibility. If you define a dispatcher expecting it to route to the superclass method, but that method is private, the dispatcher won't work as intended.
Example (Error)
#!ek9
defines module bad.dispatchermethods
defines class
SomeBaseClass as open
//PRIVATE method - hidden from subclasses
@Error: FULL_RESOLUTION: DISPATCHER_PRIVATE_IN_SUPER
private process()
-> arg as Integer
assert arg?
BadDispatcher5 extends SomeBaseClass
//Dispatcher entry point
process() as dispatcher
-> arg as Any
assert arg?
//This dispatcher CANNOT call the private super method
Solution
#!ek9
defines module bad.dispatchermethods
defines class
SomeBaseClass as open
//CORRECT: Make method protected or public
protected process()
-> arg as Integer
assert arg?
GoodDispatcher extends SomeBaseClass
//CORRECT: Now dispatcher can dispatch to super method
process() as dispatcher
-> arg as Any
assert arg?
See Also
E05190: Mix Of Pure And Not Pure Constructors
Classification: MIX_OF_PURE_AND_NOT_PURE_CONSTRUCTORS
Description
A class has both pure and non-pure constructors. EK9 requires consistency: either all constructors must be pure, or none can be pure. Mixing is not allowed.
Why this matters: Purity affects how objects can be constructed and used in pure contexts. Allowing mixed constructor purity would create ambiguity about whether the class can be safely instantiated in pure contexts.
Example (Error)
#!ek9
defines module bad.pure.scenarios1
defines class
C1
prop1 as String?
prop2 <- "Steve"
//ERROR: Not pure
@Error: FULL_RESOLUTION: MIX_OF_PURE_AND_NOT_PURE_CONSTRUCTORS
default C1()
//ERROR: Not pure
@Error: FULL_RESOLUTION: MIX_OF_PURE_AND_NOT_PURE_CONSTRUCTORS
C1()
-> arg0 as String
prop1 :=? arg0
//One pure constructor - creates inconsistency
C1() as pure
->
arg0 as String
arg1 as String
prop1 :=? arg0
prop2 :=? arg1
Solutions
Option 1: Make all constructors pure
#!ek9
defines module bad.pure.scenarios1
defines class
C1
prop1 as String?
prop2 as String?
//CORRECT: All constructors pure
default C1() as pure
C1() as pure
-> arg0 as String
prop1 :=? arg0
C1() as pure
->
arg0 as String
arg1 as String
prop1 :=? arg0
prop2 :=? arg1
Option 2: Remove pure from all constructors
#!ek9
defines module bad.pure.scenarios1
defines class
C1
prop1 as String?
prop2 <- "Steve"
//CORRECT: None pure
default C1()
C1()
-> arg0 as String
prop1 :=? arg0
C1()
->
arg0 as String
arg1 as String
prop1 := arg0
prop2 := arg1
See Also
E05200: Incompatible Genus Constructor
Classification: INCOMPATIBLE_GENUS_CONSTRUCTOR
Description
Attempting to manually instantiate a construct whose genus (type category) cannot be constructed directly. Certain genus types are managed by the EK9 runtime and dependency injection system and cannot be manually instantiated:
- APPLICATION - Applications are wired by the EK9 runtime
- PROGRAM - Programs are entry points, not constructable objects
- SERVICE - Services are registered and managed by the runtime
- COMPONENT - Components are created via dependency injection
Why this matters: These constructs have special lifecycles and are managed by the EK9 runtime. Manual construction would bypass the framework's management, violating the designed architecture.
Example (Error)
#!ek9
defines module bad.applicationcalls.examples1
defines application
ServiceApplication
//Application definition
defines program
AnotherProgram
//Program definition
YetAnotherProgram
//ERROR: Cannot manually construct APPLICATION genus
@Error: FULL_RESOLUTION: INCOMPATIBLE_GENUS_CONSTRUCTOR
app <- ServiceApplication()
//ERROR: Cannot manually construct PROGRAM genus
@Error: FULL_RESOLUTION: INCOMPATIBLE_GENUS_CONSTRUCTOR
otherProg <- AnotherProgram()
Solution
#!ek9
defines module bad.applicationcalls.examples1
defines application
ServiceApplication
//Application definition
defines program
//CORRECT: Use 'with application' to wire the application
ServiceProgram with application of ServiceApplication
stdout <- Stdout()
stdout.println("Starting ServiceApplication")
//Programs are entry points - they cannot be constructed,
//but they can be executed by the EK9 runtime
See Also
Phase 08: PRE_IR_CHECKS (Code Flow Analysis)
These errors are detected during EK9's comprehensive code flow analysis phase. EK9 analyzes all possible execution paths to ensure variables are properly initialized before use, eliminating entire classes of runtime errors at compile time.
E08020: Used Before Initialized
Classification: USED_BEFORE_INITIALISED
Description
EK9's flow analysis detected that a variable might be used before it has been assigned a value on all possible code paths. This is one of the most common errors and represents EK9's commitment to eliminating null pointer exceptions and uninitialized variable bugs at compile time.
Example (Error)
#!ek9
defines module badclass.method.initialisations
defines class
BasicClass
operator < as pure
-> arg0 BasicClass
@Error: PRE_IR_CHECKS: RETURN_NOT_ALWAYS_INITIALISED
<- rtn as Boolean?
@Error: PRE_IR_CHECKS: USED_BEFORE_INITIALISED
assert rtn?
Why this fails: The return value rtn is declared but never
assigned a value before being used in the assert statement. The compiler's flow analysis
detects that assert rtn? accesses an uninitialized variable, which would
cause undefined behavior.
Solutions
Option 1: Initialize before conditional
#!ek9
demo()
result <- "default" //Initialize first
if condition
result: "value"
assert result? //Always initialized
Option 2: Add else clause
#!ek9
demo()
result as String?
if condition
result: "value"
else
result: "default" //Initialize on else path too
assert result?
Option 3: Use guard expression
#!ek9
demo()
if result <- getValue() //Only executes if set
assert result?
Common mistake: Forgetting that if/else branches create separate code paths. EK9 checks ALL paths to ensure the variable is initialized regardless of which path is taken at runtime.
See Also
- E08050: Return Not Always Initialized
- E08070: Never Initialized
- Guard Expressions
- Variable Initialization
E08050: Return Not Always Initialized
Classification: RETURN_NOT_ALWAYS_INITIALISED
Description
The return value of a function or method is not initialized on all possible code paths. EK9 requires that if a function declares a return value, it must be initialized on every possible execution path through the function.
Example (Error)
#!ek9
defines module badclass.method.initialisations
defines class
BasicClass
operator < as pure
-> arg0 BasicClass
@Error: PRE_IR_CHECKS: RETURN_NOT_ALWAYS_INITIALISED
<- rtn as Boolean?
@Error: PRE_IR_CHECKS: USED_BEFORE_INITIALISED
assert rtn?
Solution
#!ek9
calculateScore()
<- result as Integer: 0 //Default value
if someCondition
result: 100
//result always has a value
See Also
E08070: Never Initialized
Classification: NEVER_INITIALISED
Description
A variable or property has been declared but is never assigned a value anywhere in the code. This is different from E08020 (might be used before initialized) - this error means the variable is NEVER initialized at all.
Note: This error applies to class/component properties, not local variables. For properties, EK9 requires either initialization at declaration or in a constructor.
Example (Error)
#!ek9
defines class
ComponentWithUninitProp
@Error: PRE_IR_CHECKS: NEVER_INITIALISED
data as String? //Declared but never initialized
useData()
@Error: PRE_IR_CHECKS: NOT_INITIALISED_BEFORE_USE
result <- data + " processed" //Using uninitialized property
Solutions
Option 1: Initialize at declaration
#!ek9
defines class
ComponentWithInitProp
data as String: "default value" //Initialize immediately
useData()
result <- data + " processed" //OK
Option 2: Initialize in constructor
#!ek9
defines class
ComponentWithConstructor
data as String?
default ComponentWithConstructor()
data: "initialized in constructor"
useData()
result <- data + " processed" //OK
See Also
E08010: Used Before Defined
Classification: USED_BEFORE_DEFINED
Description
A variable is being used in an expression before it has been declared (lexically defined) in the code. This is a forward reference error - the variable name appears in an expression before the declaration statement that creates it. EK9 requires strict lexical ordering: declarations must appear before usage within the same scope. Unlike E08020 (used before initialized), this error means the variable declaration itself comes textually after its first use in the source code, creating an impossible reference.
Example (Error)
#!ek9
defines module bad.blockvariable.uses
defines class
CheckBase as open
usedBeforeDefinition()
@Error: FULL_RESOLUTION: USED_BEFORE_DEFINED
someVar <- AVariableThatHasNotYetBeenDefined
assert someVar?
//Now define the variable - but after the use
AVariableThatHasNotYetBeenDefined <- "Now Defined"
Solution
#!ek9 demo() myValue <- 5 //Declare first result <- myValue + 10 //Then use
See Also
E08030: Unsafe Method Access
Classification: UNSAFE_METHOD_ACCESS
Description
An optional variable (declared with ?) is being used to call methods
without first checking if it's set. EK9 requires explicit null-safety checks
before accessing methods on optional types.
Example (Error)
#!ek9
defines module error.on.optional.access
defines function
testInvalidUsingTernary1()
o <- Optional("Steve")
@Error: PRE_IR_CHECKS: UNSAFE_METHOD_ACCESS
value <- o? <- "Something" else o.get()
assert value?
Solutions
Option 1: Use guard expression
#!ek9
processName()
name as String?
if name? //Check if set
length <- name.length() //Safe access
Option 2: Use null-safe method access
#!ek9 processName() name as String? //Safe method access - returns Optional of Integer length <- name?.length()
See Also
E08040: No Reassignment Within Safe Access
Classification: NO_REASSIGNMENT_WITHIN_SAFE_ACCESS
Description
Within a safe access block (after checking variable?), you cannot
reassign the variable. This would invalidate the safety guarantee that the
compiler provides.
Example (Error)
#!ek9
defines module error.on.result.reassignment
defines function
getExplicitResult()
<- rtn <- Result("Steve", Integer())
o <- getExplicitResult()
if o?
//We should not be allowed to reassign in here, this scope is marked safe access
//This would break that and make logic very complex if we were to deal with work around.
@Error: PRE_IR_CHECKS: NO_REASSIGNMENT_WITHIN_SAFE_ACCESS
o: Result("Stephen2", 2)
val <- o.ok()
assert val?
Solution
#!ek9
demo()
data as String?
if data?
result <- data.length() //Use the value, don't reassign
//Reassignment OK outside the safe access block
data := "new value"
See Also
E08060: Not Initialized Before Use
Classification: NOT_INITIALISED_BEFORE_USE
Description
A variable is being used before it has been initialized on the current code path. This is similar to E08020, but specifically applies to property access within classes.
Example (Error)
#!ek9
defines class
DataProcessor
value as Integer?
process()
@Error: PRE_IR_CHECKS: NOT_INITIALISED_BEFORE_USE
result <- value * 2 //value never initialized
Solution
#!ek9
defines class
DataProcessor
value as Integer: 0 //Initialize at declaration
process()
result <- value * 2 //OK
See Also
E08080: Self Assignment
Classification: SELF_ASSIGNMENT
Description
A variable is being assigned to itself (x := x). This is almost
always a mistake and has no effect - the variable already contains its own value.
EK9 flags this as an error to help catch logic errors, typos, and copy-paste mistakes.
This includes both regular assignment (:=) and copy assignment (:=:),
as copying a value over itself is equally pointless.
Example (Error)
#!ek9
defines module bad.blockvariable.uses
defines class
CheckBase as open
selfAssignmentInBlockVariable()
someBlockVariable <- 1
@Error: FULL_RESOLUTION: SELF_ASSIGNMENT
someBlockVariable: someBlockVariable
//Really a copy over self, but still a self assignment
@Error: FULL_RESOLUTION: SELF_ASSIGNMENT
someBlockVariable :=: someBlockVariable
Common Cause
This error often occurs when copy-pasting code or during refactoring. Check if you meant to assign a different variable or perform an operation.
Solution
#!ek9 demo() value <- 10 newValue <- 20 value := newValue //Assign different variable
E08090: Not Referenced
Classification: NOT_REFERENCED
Description
A variable has been declared and possibly initialized, but is never referenced (used) anywhere in the code. This indicates dead code, wasted computation, or a forgotten implementation. EK9 requires all declared variables to be used, promoting cleaner code and helping identify logic errors where a variable was intended to be used but was accidentally omitted. This check applies to both initialized variables and variable-only declarations.
Example (Error)
#!ek9
defines module bad.blockvariable.uses
defines class
CheckBase as open
unReferencedBlockVariable()
@Error: FULL_RESOLUTION: NOT_REFERENCED
someUnReferencedVariable1 <- 1
@Error: FULL_RESOLUTION: NOT_REFERENCED
someUnReferencedVariable2 as String?
aReferencedVariable <- 2
assert aReferencedVariable?
Solutions
If truly unused: Remove the variable declaration
#!ek9 demo() result <- calculateValue() //Removed unused variable
If needed: Use the variable in your logic
#!ek9 demo() multiplier <- 42 result <- calculateValue() * multiplier //Now used
E08100: No Pure Reassignment
Classification: NO_PURE_REASSIGNMENT
Description
Within a function/method/constructor marked as pure, you cannot reassign variables
using := (reassignment operator). Pure functions must be side-effect free, and
reassignment is considered a mutation side effect. This maintains referential transparency - pure
functions always produce the same output for the same input. Use :=?
(conditional/guarded assignment) for initial assignment only when the variable is unset, or create
new variables instead of reassigning existing ones.
Example (Error)
#!ek9
defines module bad.pure.scenarios1
defines class
C1 as pure
prop1 as String?
prop2 as Integer?
C1() as pure
-> arg0 as String
-> arg1 as Integer
//So if a constructor is marked as pure use the :=? to assign
//But this means you cannot have default values really.
//Well you can but use multiple constructors
@Error: FULL_RESOLUTION: NO_PURE_REASSIGNMENT
prop2 := arg1
Solution
#!ek9 calculate() as pure <- result as Integer: 0 result <- 10 //Use initial assignment instead //No reassignment needed
See Also
E08110: No Incoming Argument Reassignment
Classification: NO_INCOMING_ARGUMENT_REASSIGNMENT
Description
Function parameters/arguments cannot be reassigned using := or :=?.
This prevents confusion about whether a parameter represents the original input value or a
modified value during function execution. Parameters are immutable once passed to a function.
If you need to work with a potentially different value, create a new local variable instead
of attempting to reassign the incoming argument. This promotes clarity and prevents subtle
bugs where modified parameters could be mistaken for original inputs.
Example (Error)
#!ek9
defines module bad.pure.scenarios1
defines function
someMethod()
-> arg0 as String?
//We do NOT allow a null check reassignment - even in pure
//There is no need - just create a new variable as shown below
@Error: FULL_RESOLUTION: NO_INCOMING_ARGUMENT_REASSIGNMENT
arg0 :=? "Stephen"
//Use this approach instead, rather than reusing incoming arguments.
newVar <- arg0? <- arg0: "Stephen"
assert newVar?
Solution
#!ek9 process() -> value as Integer modified <- value + 10 //Create new variable instead result <- modified * 2
E08120: No Mutation In Pure Context
Classification: NO_MUTATION_IN_PURE_CONTEXT
Description
Within a pure function or method, you cannot call mutating operators or methods
on objects. Mutating operators like +=, -=, ++, etc.
modify object state in place, which violates the purity constraint. Pure functions must have
no side effects, including modifying object state - they can only compute and return new values.
This ensures referential transparency and makes pure functions safe for optimization, parallelization,
and memoization.
Example (Error)
#!ek9
defines module bad.pure.scenarios2
defines class
C2 as pure
prop1 <- R1()
C2() as pure
r1 <- R1()
@Error: FULL_RESOLUTION: NO_PURE_REASSIGNMENT
r1.value: "James"
//While this is a field/property - it is not a property on this type
@Error: FULL_RESOLUTION: NO_MUTATION_IN_PURE_CONTEXT
r1.value += "Brown"
Solution
#!ek9 processData() as pure -> list as List of String //Create new list instead of mutating <- result as List of String: List(list) result += "item" //OK - mutating local copy
See Also
E08130: Non-Pure Call In Pure Scope
Classification: NONE_PURE_CALL_IN_PURE_SCOPE
Description
A pure function is calling another function/method that is NOT
marked as pure. Pure functions can only call other pure functions
to maintain the guarantee of no side effects.
Example (Error)
#!ek9
impureFunction()
<- result as String: "side effect"
stdout.println("This has side effects!")
calculate() as pure
@Error: PRE_IR_CHECKS: NONE_PURE_CALL_IN_PURE_SCOPE
value <- impureFunction() //Can't call non-pure function
Solution
#!ek9 pureHelper() as pure <- result as Integer: 42 calculate() as pure value <- pureHelper() //OK - calling pure function
See Also
E08140: Component Injection In Pure
Classification: COMPONENT_INJECTION_IN_PURE
Description
Dependency injection (using ! syntax) is not allowed in pure
functions, methods, or constructors. Dependency injection involves accessing external
state managed by the DI container at runtime, which introduces side effects and breaks
referential transparency. Pure functions must be deterministic and self-contained,
depending only on their input parameters to produce output.
Example (Error)
#!ek9
defines module bad.injection.examples
defines component
Comp1
default Comp1()
defines function
TryToInjectInPure() as pure
@Error: FULL_RESOLUTION: COMPONENT_INJECTION_IN_PURE
injected as Comp1!
assert injected?
Solution
Remove the pure marker or pass dependencies as constructor parameters
instead of using injection.
#!ek9
defines class
Processor //Not marked pure
service as IService: Injected //OK
See Also
E08150: Component Injection Of Non-Abstract
Classification: COMPONENT_INJECTION_OF_NON_ABSTRACT
Description
Dependency injection requires an abstract base type (interface/trait/abstract component).
You cannot inject concrete (non-abstract) component implementations directly using the
! syntax. This violates the dependency inversion principle - injection should
depend on abstractions, not concretions. The DI container needs an abstract type to determine
which concrete implementation to provide based on configuration. Use abstract components for
injection, allowing different concrete implementations to be wired at deployment time.
Example (Error)
#!ek9
defines module bad.injection.examples
defines component
Comp2
default Comp2()
defines function
TryToInjectNonAbstractComponent()
//This won't work because it's not abstract (but it is component)
@Error: FULL_RESOLUTION: COMPONENT_INJECTION_OF_NON_ABSTRACT
injected as Comp2!
assert injected?
defines class
Consumer
@Error: PRE_IR_CHECKS: COMPONENT_INJECTION_OF_NON_ABSTRACT
service as ConcreteService: Injected //Can't inject concrete type
Solution
#!ek9
defines trait
IService
process()
<- result as String?
defines class
ConcreteService with trait of IService
override process()
<- result as String: "done"
defines class
Consumer
service as IService: Injected //OK - injecting abstract type
See Also
E08160: Component Injection Not Possible
Classification: COMPONENT_INJECTION_NOT_POSSIBLE
Description
Dependency injection (using ! syntax) is being attempted in a context where
it's not supported. Injection is only allowed for properties/fields in classes and components,
not in function-local variables, method parameters, or primitive types. The dependency injection
system needs stable, class-scoped storage to manage component lifecycles. Use constructor
parameters or method parameters to pass dependencies into functions, or restructure code
to use class-based DI if needed.
Example (Error)
#!ek9
defines module bad.injection.examples
defines function
TryToInjectNonComponent()
//This won't work because it's not a component
@Error: FULL_RESOLUTION: COMPONENT_INJECTION_NOT_POSSIBLE
injected as String!
assert injected?
Solution
Pass dependencies as parameters or use class-based dependency injection.
#!ek9
defines class
Processor
service as IService: Injected //OK - injection in class
process()
result <- service.execute()
See Also
E08170: Reassignment Of Injected Component
Classification: REASSIGNMENT_OF_INJECTED_COMPONENT
Description
Properties marked for dependency injection (using ! syntax) cannot be directly
reassigned using :=. Once a component is injected by the DI container, manually
replacing it would break the dependency injection contract and lifecycle management. The DI
container maintains control over injected component instances. If you need conditional
initialization or want to provide a default, use :=? (guarded/conditional
assignment) which only assigns if the variable is still unset after injection attempts.
Example (Error)
#!ek9
defines module bad.injection.examples
defines component
Comp1
default Comp1()
defines function
TryToDirectlyReassignInjectedComponent()
//So this is the injection
injected as Comp1!
//But cannot do this - directly reassign use :=? to only assign if null.
@Error: FULL_RESOLUTION: REASSIGNMENT_OF_INJECTED_COMPONENT
injected := DefaultComp()
//Now just ensure it is referenced after a potential assignment (else why did we reassign it)
assert injected?
Solution
#!ek9
defines class
ServiceConsumer
service as IService: Injected
reconfigure()
//Use conditional assignment if you need fallback
service :=? alternativeService //OK - only if not already set
See Also
E08180: Not Initialized In Any Way
Classification: NOT_INITIALISED_IN_ANY_WAY
Description
A class property is neither initialized at declaration, nor in a constructor, nor marked for dependency injection. EK9 requires all properties to be initialized through one of these three mechanisms.
Example (Error)
#!ek9
defines class
BadComponent
@Error: PRE_IR_CHECKS: NOT_INITIALISED_IN_ANY_WAY
data as String? //Not initialized anywhere
process()
result <- data.length()
Solutions
Option 1: Initialize at declaration
#!ek9
defines class
GoodComponent
data as String: "default" //Initialized
Option 2: Initialize in constructor
#!ek9
defines class
GoodComponent
data as String?
default GoodComponent()
data: "initialized"
Option 3: Use dependency injection
#!ek9
defines class
GoodComponent
data as IDataProvider: Injected //Injected
See Also
Phase 09: PLUGIN_RESOLUTION
Plugin resolution errors occur when the compiler validates plugin integration points.
E09010: Inappropriate Use
Classification: INAPPROPRIATE_USE
Description
A language construct is being used in a context where it's not appropriate or allowed. This is a general error that covers various misuses of EK9 constructs in inappropriate contexts.
Example (Error)
#!ek9 defines module example @Error: PLUGIN_RESOLUTION: INAPPROPRIATE_USE result <- this //Using 'this' outside of a class/method context
Solution
#!ek9
defines module example
defines class
Example
getValue()
<- result as String: $"Value from ${this}" //OK - 'this' in class method
See Also
Phase 10: IR_GENERATION
IR generation errors occur during the intermediate representation creation phase, when the compiler translates validated source code into its internal representation.
E10010: Return Type Void Meaningless
Classification: RETURN_TYPE_VOID_MEANINGLESS
Description
A function or method declares a void return type but attempts to assign
the result to a variable. Void functions don't return values, so assignment is meaningless.
Example (Error)
#!ek9
processData()
//No return value declared (implicitly void)
stdout.println("Processing")
demo()
@Error: IR_GENERATION: RETURN_TYPE_VOID_MEANINGLESS
result <- processData() //Cannot assign void result
Solution
#!ek9 processData() <- result as String: "Processing complete" //Declare return type demo() result <- processData() //OK - function returns a value
See Also
E10020: Stream Type Not Defined
Classification: STREAM_TYPE_NOT_DEFINED
Description
Void cannot be used in stream pipelines. Each stage of a stream must produce a value that can be passed to the next stage. Void-returning functions break the pipeline.
Example (Error)
#!ek9 processItem() -> item as String stdout.println(item) //No return value (void) demo() items <- ["one", "two", "three"] @Error: IR_GENERATION: STREAM_TYPE_NOT_DEFINED cat items | map with processItem //Void breaks pipeline
Solution
#!ek9 processItem() -> item as String <- result as String: item.toUpperCase() //Return transformed value demo() items <- ["one", "two", "three"] cat items | map with processItem //OK - returns String
See Also
E10030: Constructor Used On Abstract Type
Classification: CONSTRUCTOR_USED_ON_ABSTRACT_TYPE
Description
Direct instantiation of an abstract type (trait, abstract class) using a constructor is not permitted. Abstract types must be extended/implemented by concrete types, which are then instantiated.
Example (Error)
#!ek9
defines trait
Processable
process()
<- result as String?
demo()
@Error: IR_GENERATION: CONSTRUCTOR_USED_ON_ABSTRACT_TYPE
obj <- Processable() //Cannot instantiate trait directly
Solution
#!ek9
defines trait
Processable
process()
<- result as String?
defines class
ConcreteProcessor with trait of Processable
override process()
<- result as String: "Processed"
demo()
obj <- ConcreteProcessor() //OK - instantiate concrete implementation
See Also
Phase 11: IR_ANALYSIS
IR analysis errors are detected during complexity analysis and code quality checks on the intermediate representation.
E11010: Excessive Complexity
Classification: EXCESSIVE_COMPLEXITY
Description
A function or method has excessive cyclomatic complexity and should be refactored into smaller, more maintainable functions. High complexity makes code difficult to understand, test, and maintain.
Example (Error)
#!ek9
@Error: IR_ANALYSIS: EXCESSIVE_COMPLEXITY
processData()
-> data as Integer
<- result as String?
if data < 0
if data < -10
if data < -20
result: "Very negative"
else
result: "Quite negative"
else
result: "Slightly negative"
else
if data > 10
if data > 20
result: "Very positive"
else
result: "Quite positive"
else
result: "Slightly positive"
//... many more nested conditions
Solution
#!ek9 classifyNegative() -> value as Integer <- result as String: value < -20 ? "Very negative" : "Quite negative" classifyPositive() -> value as Integer <- result as String: value > 20 ? "Very positive" : "Quite positive" processData() -> data as Integer <- result as String: data < 0 ? classifyNegative(data) : classifyPositive(data)
Best Practice: Keep functions focused and simple. If a function becomes complex, extract logical groups into separate helper functions. Aim for cyclomatic complexity < 10 for most functions.
See Also
E11011: Excessive Nesting
Classification: EXCESSIVE_NESTING
Description
A function, method, or operator has excessive nesting depth of control structures. Deep nesting makes code difficult to read, understand, and maintain. The maximum allowed nesting depth is 10 levels.
Example (Error)
#!ek9
@Error: PRE_IR_CHECKS: EXCESSIVE_NESTING
processData()
-> value as Integer
<- result as Integer: 0
if value > 0
if value > 1
if value > 2
if value > 3
if value > 4
if value > 5
if value > 6
if value > 7
if value > 8
if value > 9
if value > 10 //11th level - exceeds limit!
result: 11
Solution
#!ek9
//Extract nested logic into separate functions
processDeepValue()
-> value as Integer
<- result as Integer: value > 10 ? 11 : 10
processData()
-> value as Integer
<- result as Integer: 0
if value > 0
if value > 1
if value > 2
if value > 3
if value > 4
result: processDeepValue(value)
Best Practice: Keep nesting shallow by extracting deeply nested logic into helper functions. If you find yourself nesting more than 3-4 levels deep, consider refactoring. Guard clauses and early returns (via guard expressions) can also help reduce nesting.
See Also
Common/Multi-Phase Errors (E50xxx)
These errors can be triggered in multiple compilation phases because they represent fundamental issues that EK9 checks throughout the compilation process.
E50001: Not Resolved
Classification: NOT_RESOLVED
Type: Common (Multi-Phase)
Can occur in phases:
- Phase 2 (SYMBOL_DEFINITION): Generic type parameters
- Phase 3 (REFERENCE_CHECKS): Identifier lookups
- Phase 4 (EXPLICIT_TYPE_SYMBOL_DEFINITION): Parameterized type arguments
Description
A symbol (variable, type, function, method, etc.) could not be found in the current scope. The specific context depends on which compilation phase detected the error.
Example (Error)
#!ek9
defines module example
demo()
@Error: REFERENCE_CHECKS: NOT_RESOLVED
result <- unknownVariable //This variable doesn't exist
Solution
#!ek9
defines module example
demo()
myVariable <- "value" //Declare first
result <- myVariable //Now it resolves
Common Causes
- Typo in variable/function/type name
- Using a symbol before declaring it
- Missing module import/reference
- Scope issues (trying to access a local variable from outer scope)
See Also
E50010: Type Not Resolved
Classification: TYPE_NOT_RESOLVED
Type: Common (Multi-Phase)
Can occur in phases:
- Phase 2 (SYMBOL_DEFINITION): Type declarations
- Phase 3 (REFERENCE_CHECKS): Type references
- Phase 4 (EXPLICIT_TYPE_SYMBOL_DEFINITION): Template/generic type arguments
Description
A type (class, record, function, trait, etc.) could not be found or resolved. This often happens when referencing a type that hasn't been defined or imported.
Example (Error)
#!ek9
defines module example
demo()
@Error: TYPE_NOT_RESOLVED
myObject as UnknownType? //This type doesn't exist
Solution
#!ek9
defines module example
defines record
MyType
value as Integer?
demo()
myObject as MyType? //Now it resolves
See Also
E50020: Incompatible Genus
Classification: INCOMPATIBLE_GENUS
Type: Common (Multi-Phase)
Description
An operation requires a specific genus (type category) but received a different one. EK9 has distinct genus categories: CLASS, RECORD, TRAIT, FUNCTION, COMPONENT, SERVICE, etc. Operations designed for one genus cannot accept another.
Example (Error)
#!ek9
defines function
MyFunction()
<- result as String: "function result"
defines class
MyClass
process() //Expecting class genus
-> fn as MyClass
<- result as String: "processed"
demo()
@Error: INCOMPATIBLE_GENUS
obj <- MyClass()
result <- obj.process(MyFunction) //Passing function to class parameter
Solution
#!ek9
defines class
MyClass
process() //Use matching genus
-> other as MyClass
<- result as String: "processed"
demo()
obj1 <- MyClass()
obj2 <- MyClass()
result <- obj1.process(obj2) //Both are class genus
See Also
E50030: Incompatible Types
Classification: INCOMPATIBLE_TYPES
Type: Common (Multi-Phase)
Description
Two types are incompatible and cannot be used together in the given context. This includes assignment incompatibility, parameter type mismatches, and return type conflicts.
Example (Error)
#!ek9
defines module example
demo()
stringValue <- "text"
@Error: INCOMPATIBLE_TYPES
numberValue as Integer: stringValue //Cannot assign String to Integer
Solution
#!ek9
defines module example
demo()
stringValue <- "42"
numberValue as Integer: Integer(stringValue) //Convert explicitly
Common Causes
- Assigning incompatible types without conversion
- Passing wrong type to function/method parameter
- Returning wrong type from function
- Missing type conversion or coercion
See Also
E50040: Cannot Be Abstract
Classification: CANNOT_BE_ABSTRACT
Type: Common (Multi-Phase)
Description
A construct has been marked as abstract in a context where abstract is not allowed. Only specific constructs (classes, functions, methods) can be abstract, and only in appropriate contexts.
Example (Error)
#!ek9
defines module example
@Error: CANNOT_BE_ABSTRACT
defines record as abstract //Records cannot be abstract
MyRecord
value as Integer?
Solution
#!ek9
defines module example
defines record //Remove abstract modifier
MyRecord
value as Integer?
Abstract Rules
- Can be abstract: Classes, Functions, Methods
- Cannot be abstract: Records, Components, Services, Traits (use different mechanisms)
See Also
E50050: Duplicate Variable
Classification: DUPLICATE_VARIABLE
Type: Common (Multi-Phase)
Description
A variable or constant has been declared multiple times in the same scope. EK9 does not allow redeclaration of variables within the same scope.
Example (Error)
#!ek9
defines module example
demo()
value <- 42
@Error: DUPLICATE_VARIABLE
value <- 100 //Redeclaration with <- not allowed
Solution 1: Use Assignment Operator
#!ek9
defines module example
demo()
value <- 42 //Declaration
value := 100 //Assignment (use := not <-)
Solution 2: Use Different Name
#!ek9
defines module example
demo()
originalValue <- 42
newValue <- 100 //Different variable
See Also
E50060: Method Not Resolved
Classification: METHOD_NOT_RESOLVED
Type: Common (Multi-Phase)
Description
A method or function call could not be resolved. This typically means no matching method/function exists with the given name and parameter types.
Example (Error)
#!ek9
defines class
MyClass
doSomething()
-> value as Integer
<- result as String: $value
demo()
obj <- MyClass()
@Error: METHOD_NOT_RESOLVED
result <- obj.doSomethingElse() //Method doesn't exist
Solution
#!ek9
defines class
MyClass
doSomething()
-> value as Integer
<- result as String: $value
demo()
obj <- MyClass()
result <- obj.doSomething(42) //Call existing method
Common Causes
- Typo in method name
- Wrong number of parameters
- Wrong parameter types (no matching overload)
- Calling private method from outside class
See Also
E50070: Bad Abstract Function Use
Classification: BAD_ABSTRACT_FUNCTION_USE
Type: Common (Multi-Phase)
Description
An abstract function is being used in a way that's not permitted. Abstract functions can only be used in specific contexts - they cannot be called directly and must be implemented or passed as higher-order function references.
Example (Error)
#!ek9
defines function as abstract
Processor
-> input as String
<- output as String
demo()
@Error: BAD_ABSTRACT_FUNCTION_USE
result <- Processor("test") //Cannot call abstract function directly
Solution
#!ek9
defines function as abstract
Processor
-> input as String
<- output as String
defines function as Processor
UpperCaseProcessor
-> input as String
<- output as String: input.upperCase()
demo()
result <- UpperCaseProcessor("test") //Call concrete implementation
See Also
E50080: Cannot Call Abstract Type
Classification: CANNOT_CALL_ABSTRACT_TYPE
Type: Common (Multi-Phase)
Description
Attempted to instantiate or call an abstract class, function, or method directly. Abstract types must be extended/implemented before they can be used.
Example (Error)
#!ek9
defines class as abstract
Shape
abstract area()
<- result as Float
demo()
@Error: CANNOT_CALL_ABSTRACT_TYPE
shape <- Shape() //Cannot instantiate abstract class
Solution
#!ek9
defines class as abstract
Shape
abstract area()
<- result as Float
defines class as open extends Shape
Circle
radius <- 5.0
override area()
<- result as Float: 3.14159 * radius * radius
demo()
shape <- Circle() //Instantiate concrete class
See Also
E50090: Incompatible Parameter Genus
Classification: INCOMPATIBLE_PARAMETER_GENUS
Type: Common (Multi-Phase)
Description
One or more function/method parameters have incompatible genus (type category). When calling a function, all arguments must match the genus of the corresponding parameters.
Example (Error)
#!ek9
defines record
DataRecord
value as Integer?
defines function
process()
-> record as DataRecord
<- result as String: $record.value
defines class
DataClass
value as Integer: 0
demo()
data <- DataClass()
@Error: INCOMPATIBLE_PARAMETER_GENUS
result <- process(data) //Passing CLASS to RECORD parameter
Solution
#!ek9
defines record
DataRecord
value as Integer?
defines function
process()
-> record as DataRecord
<- result as String: $record.value
demo()
data <- DataRecord(42)
result <- process(data) //Matching genus
See Also
E50100: Incompatible Category
Classification: INCOMPATIBLE_CATEGORY
Type: Common (Multi-Phase)
Description
A symbol has an incompatible category for the operation being performed. Categories include METHOD, VARIABLE, FUNCTION, TYPE, etc. Each context requires specific categories.
Example (Error)
#!ek9
defines class
Calculator
add()
->
a as Integer
b as Integer
demo()
calc <- Calculator()
@Error: INCOMPATIBLE_CATEGORY
methodRef <- calc.add //Cannot reference method without call context
Solution
#!ek9
defines function
add()
->
a as Integer
b as Integer
demo()
functionRef <- add //Functions can be referenced
result <- functionRef(5, 3)
See Also
Testing Directives (E50200-E50310)
These errors relate to EK9's testing directive system. Directives like @Error,
@Resolved, @IR, and @Complexity are used in test
files to validate compiler behavior. These errors indicate mismatches between expected
and actual compiler behavior.
E50200: Unknown Directive
Classification: UNKNOWN_DIRECTIVE
Type: Testing Infrastructure
Description
An unrecognized directive was encountered in test code. Valid directives include
@Error, @Resolved, @IR, @BYTECODE,
and @Complexity.
Example (Error)
#!ek9
defines module test
demo()
@Error: UNKNOWN_DIRECTIVE
@InvalidDirective: SOME_ERROR //Not a valid directive
value <- unknownVariable
Solution
#!ek9
defines module test
demo()
@Error: NOT_RESOLVED //Use valid directive
value <- unknownVariable
Valid Directives
@Error: PHASE: CLASSIFICATION- Expected compilation error@Resolved: SYMBOL_DEFINITION- Symbol resolution validation@IR: SYMBOL_DEFINITION: TYPE: symbolName- IR generation check@BYTECODE: symbolName- Bytecode generation check@Complexity: N- Cyclomatic complexity check
See Also
E50210: Directive Missing
Classification: DIRECTIVE_MISSING
Type: Testing Infrastructure
Description
The compiler expected a directive but none was found. This typically occurs during test validation when the test framework expects specific markers.
Example (Error)
#!ek9
//Test expects @Error directive but it's missing
defines module test
demo()
@Error: DIRECTIVE_MISSING
value <- unknownVariable //Should have @Error directive on this line
Solution
#!ek9
defines module test
demo()
@Error: NOT_RESOLVED //Add expected directive
value <- unknownVariable
See Also
E50220: Directive Wrong Classification
Classification: DIRECTIVE_WRONG_CLASSIFICATION
Type: Testing Infrastructure
Description
An @Error directive specified the wrong error classification.
The actual error generated by the compiler doesn't match the expected classification.
Example (Error)
#!ek9
defines module test
demo()
@Error: DIRECTIVE_WRONG_CLASSIFICATION
@Error: TYPE_MISMATCH //Wrong classification
value <- unknownVariable //Actually generates NOT_RESOLVED
Solution
#!ek9
defines module test
demo()
@Error: NOT_RESOLVED //Correct classification
value <- unknownVariable
Tip: Run the compiler without directives first to see what
actual error is generated, then add the matching @Error directive.
See Also
E50230: Error Missing
Classification: ERROR_MISSING
Type: Testing Infrastructure
Description
An @Error directive was specified but the compiler did not generate
the expected error. The code compiled successfully when it was expected to fail.
Example (Error)
#!ek9
defines module test
demo()
@Error: ERROR_MISSING
@Error: NOT_RESOLVED
value <- "valid code" //This is valid, no error generated
Solution
#!ek9
defines module test
demo()
value <- "valid code" //Remove @Error directive for valid code
testError()
@Error: NOT_RESOLVED
value <- unknownVariable //Actually generates expected error
See Also
E50240: Directive Symbol Complexity
Classification: DIRECTIVE_SYMBOL_COMPLEXITY
Type: Testing Infrastructure
Description
A @Complexity directive specified a different complexity value than
what the compiler calculated for the function/method.
Example (Error)
#!ek9
defines module test
@Error: DIRECTIVE_SYMBOL_COMPLEXITY
@Complexity: 5 //Wrong complexity value
simpleFunction()
<- result as String: "simple" //Actual complexity is 1
Solution
#!ek9
defines module test
@Complexity: 1 //Correct complexity
simpleFunction()
<- result as String: "simple"
See Also
E50250: Directive Symbol Not Resolved
Classification: DIRECTIVE_SYMBOL_NOT_RESOLVED
Type: Testing Infrastructure
Description
A @Resolved directive expected a symbol to be resolved, but it wasn't.
This validates that symbol resolution is working correctly in test code.
Example (Error)
#!ek9
defines module test
demo()
@Error: DIRECTIVE_SYMBOL_NOT_RESOLVED
@Resolved: SYMBOL_DEFINITION
value <- unknownVariable //Symbol not resolved as expected
Solution
#!ek9
defines module test
demo()
value <- 42 //Define symbol first
@Resolved: SYMBOL_DEFINITION
result <- value //Now symbol resolves
See Also
E50260: Directive Hierarchy Not Resolved
Classification: DIRECTIVE_HIERARCHY_NOT_RESOLVED
Type: Testing Infrastructure
Description
A directive expected a type hierarchy to be resolved (inheritance, trait implementation) but the hierarchy was not properly established.
Example (Error)
#!ek9
defines module test
@Error: DIRECTIVE_HIERARCHY_NOT_RESOLVED
defines class extends UnknownBase //Base not resolved
@Resolved: TYPE_HIERARCHY_CHECKS
MyClass
value as Integer?
Solution
#!ek9
defines module test
defines class
BaseClass
value as Integer?
defines class extends BaseClass
@Resolved: TYPE_HIERARCHY_CHECKS
MyClass
extraValue as String?
See Also
E50270: Directive Symbol Category Mismatch
Classification: DIRECTIVE_SYMBOL_CATEGORY_MISMATCH
Type: Testing Infrastructure
Description
A directive validation expected a specific symbol category (METHOD, VARIABLE, FUNCTION, TYPE) but found a different category.
Example (Error)
#!ek9
defines module test
@Error: DIRECTIVE_SYMBOL_CATEGORY_MISMATCH
@IR: SYMBOL_DEFINITION: METHOD: myFunction //Wrong category
myFunction() //This is a FUNCTION not METHOD
<- result as String: "test"
Solution
#!ek9
defines module test
@IR: SYMBOL_DEFINITION: FUNCTION: myFunction //Correct category
myFunction()
<- result as String: "test"
See Also
E50280: Directive Symbol Genus Mismatch
Classification: DIRECTIVE_SYMBOL_GENUS_MISMATCH
Type: Testing Infrastructure
Description
A directive validation expected a specific genus (CLASS, RECORD, FUNCTION, etc.) but the symbol has a different genus.
Example (Error)
#!ek9
defines module test
@Error: DIRECTIVE_SYMBOL_GENUS_MISMATCH
@IR: SYMBOL_DEFINITION: CLASS: MyType //Wrong genus
defines record //This is RECORD genus
MyType
value as Integer?
Solution
#!ek9
defines module test
@IR: SYMBOL_DEFINITION: RECORD: MyType //Correct genus
defines record
MyType
value as Integer?
See Also
E50290: Directive Symbol No Such Genus
Classification: DIRECTIVE_SYMBOL_NO_SUCH_GENUS
Type: Testing Infrastructure
Description
A directive referenced a genus that doesn't exist in EK9's type system.
Example (Error)
#!ek9
defines module test
@Error: DIRECTIVE_SYMBOL_NO_SUCH_GENUS
@IR: SYMBOL_DEFINITION: INTERFACE: MyType //INTERFACE is not an EK9 genus
defines class
MyType
value as Integer?
Solution
#!ek9
defines module test
@IR: SYMBOL_DEFINITION: CLASS: MyType //Use valid genus
defines class
MyType
value as Integer?
Valid EK9 Genus Types
- CLASS
- RECORD
- TRAIT
- FUNCTION
- COMPONENT
- SERVICE
- TEXT
- PROGRAM
See Also
E50300: Directive Symbol Found Unexpected Symbol
Classification: DIRECTIVE_SYMBOL_FOUND_UNEXPECTED_SYMBOL
Type: Testing Infrastructure
Description
A directive validation expected a specific symbol but found a different one. This ensures test directives are validating the correct symbols.
Example (Error)
#!ek9
defines module test
@Error: DIRECTIVE_SYMBOL_FOUND_UNEXPECTED_SYMBOL
@IR: SYMBOL_DEFINITION: FUNCTION: wrongName //Wrong symbol name
correctName() //Symbol name doesn't match directive
<- result as String: "test"
Solution
#!ek9
defines module test
@IR: SYMBOL_DEFINITION: FUNCTION: correctName //Matching symbol name
correctName()
<- result as String: "test"
See Also
E50310: Directive Error Mismatch
Classification: DIRECTIVE_ERROR_MISMATCH
Type: Testing Infrastructure
Description
The number of errors generated by the compiler doesn't match the number of
@Error directives in the test file.
Example (Error)
#!ek9
defines module test
demo()
@Error: DIRECTIVE_ERROR_MISMATCH
@Error: NOT_RESOLVED
value1 <- unknownVar1 //1 @Error directive
value2 <- unknownVar2 //But 2 actual errors!
Solution
#!ek9
defines module test
demo()
@Error: NOT_RESOLVED
value1 <- unknownVar1 //1 @Error directive
@Error: NOT_RESOLVED
value2 <- unknownVar2 //2 @Error directives matching 2 errors
See Also
Phase 06: TYPE_HIERARCHY_CHECKS
Type hierarchy checks validate generic/template types, method resolution, trait implementation, and parameter matching. This phase ensures type hierarchies are correctly structured and all type constraints are satisfied.
E06010: Generic Type Or Function Parameters Needed
Classification: GENERIC_TYPE_OR_FUNCTION_PARAMETERS_NEEDED
Description
A generic/template type or function requires type parameters but none were provided.
In EK9, generic types like List, Optional, or Dict,
and generic functions with of type declarations, must be explicitly parameterized
when instantiated. Type inference is only supported when constructor arguments provide sufficient
type information (e.g., List("Steve") infers List of String).
Example (Error)
#!ek9
defines module bad.functioncall.examples1
defines function
GenericFunction of type (S, T)
->
arg0 as S
arg1 as T
<-
rtn as Boolean: true
TestInvalidGenericFunctionUse1()
//Detect this is as being generic but omit polymorphic parameters and ensure we get an error
@Error: FULL_RESOLUTION: GENERIC_TYPE_OR_FUNCTION_PARAMETERS_NEEDED
result4A <- GenericFunction()
assert result4A?
TestInvalidGenericAggregateUse1()
@Error: FULL_RESOLUTION: GENERIC_TYPE_OR_FUNCTION_PARAMETERS_NEEDED
result5A <- List()
assert result5A?
Solution
#!ek9
defines module bad.functioncall.examples1
defines function
GenericFunction of type (S, T)
->
arg0 as S
arg1 as T
<-
rtn as Boolean: true
TestValidGenericFunctionUse()
//Provide explicit type parameters when calling generic function
result <- GenericFunction("Steve", 21)
assert result?
TestValidGenericAggregateUse()
//Provide type parameter or use constructor argument for inference
result1 <- List() of String //Explicit parameterization
result2 <- List("Steve") //Type inference from argument
assert result1? and result2?
See Also
E06020: Generic Type Or Function Parameters Incorrect
Classification: GENERIC_TYPE_OR_FUNCTION_PARAMETERS_INCORRECT
Description
The number of type parameters provided to a generic type or function does not match the
required count. Each generic type has specific parameter requirements: List and
Optional require exactly 1 type parameter, while Dict requires
exactly 2 (key and value types). This error is detected during symbol definition when the
compiler can determine parameter count mismatches from explicit type declarations.
Example (Error)
#!ek9
defines module incorrect.generic.uses
defines function
invalidNumberOfParametersExplicitType1()
//List requires 1 type parameter, given 2
@Error: SYMBOL_DEFINITION: GENERIC_TYPE_OR_FUNCTION_PARAMETERS_INCORRECT
val4 as List of (Integer, Date): List()
//Dict requires 2 type parameters, given 1
@Error: SYMBOL_DEFINITION: GENERIC_TYPE_OR_FUNCTION_PARAMETERS_INCORRECT
dict4 as Dict of (Integer): Dict()
invalidNumberOfParametersExplicitType2()
//List requires 1 type parameter, given 2
@Error: SYMBOL_DEFINITION: GENERIC_TYPE_OR_FUNCTION_PARAMETERS_INCORRECT
val5 <- List() of (Integer, Date)
//Dict requires 2 type parameters, given 1
@Error: SYMBOL_DEFINITION: GENERIC_TYPE_OR_FUNCTION_PARAMETERS_INCORRECT
dict5 <- Dict() of (Integer)
Solution
#!ek9
defines module incorrect.generic.uses
defines function
validNumberOfParameters()
//List requires exactly 1 type parameter
val4 as List of Integer: List()
val5 <- List() of String
//Dict requires exactly 2 type parameters (key, value)
dict4 as Dict of (Integer, String): Dict()
dict5 <- Dict() of (String, Date)
See Also
E06030: Generic Type Constructor Inappropriate
Classification: GENERIC_TYPE_CONSTRUCTOR_INAPPROPRIATE
Description
A constructor defined in a generic/template type has an inappropriate parameter signature for type inference. EK9 requires that constructors in generic types either have no parameters (default constructor) or have exactly one parameter of each type variable for type inference. A constructor with multiple parameters of the same type variable prevents the compiler from inferring the concrete type and is therefore inappropriate.
Example (Error)
#!ek9
defines module incorrect.parameters.on.constructors
defines class
SomeGenericType of type T
aField as T?
default SomeGenericType()
SomeGenericType()
-> param as T
aField :=: param
//This is not allowed, as it is expected a single parameter will give the compiler
//the type to infer for the generic nature of the generic/template class.
@Error: SYMBOL_DEFINITION: GENERIC_TYPE_CONSTRUCTOR_INAPPROPRIATE
SomeGenericType()
->
param1 as T
param2 as T
aField :=: param1 + param2
Solution
#!ek9
defines module incorrect.parameters.on.constructors
defines class
SomeGenericType of type T
aField as T?
//Default constructor (no parameters)
default SomeGenericType()
//Constructor with single parameter for type inference
SomeGenericType()
-> param as T
aField :=: param
See Also
E06040: Generic Type Requires Two Constructors
Classification: GENERIC_TYPE_REQUIRES_TWO_CONSTRUCTORS
Description
When defining constructors for generic/template types, EK9 requires either no explicit constructors (allowing the compiler to synthesize both default and parameterized constructors) or exactly two explicit constructors: a default constructor with no parameters and a constructor accepting one parameter for each type variable. Defining only one constructor creates ambiguity and prevents proper type inference, so it is not allowed.
Example (Error)
#!ek9
defines module bad.genericnotparameterised.example
defines class
-
For generics, either let EK9 compiler synthesize both constructors or define both yourself.
-?>
@Error: SYMBOL_DEFINITION: GENERIC_TYPE_REQUIRES_TWO_CONSTRUCTORS
C3 of type T
default C3()
-
Also in generics type inference like this is not supported (yet).
-?>
check()
-> arg0 as T
@Error: SYMBOL_DEFINITION: TYPE_INFERENCE_NOT_SUPPORTED
<- rtn <- arg0?
Solution
#!ek9
defines module bad.genericnotparameterised.example
defines class
//Option 1: Let compiler synthesize both constructors (no explicit constructors)
C3Auto of type T
check()
-> arg0 as T
<- rtn as Boolean: arg0?
//Option 2: Define both constructors explicitly
C3Manual of type T
default C3Manual()
C3Manual()
-> arg0 as T
check()
-> arg0 as T
<- rtn as Boolean: arg0?
See Also
E06050: Generic Type Requires Correct Constructor Argument Types
Classification: GENERIC_TYPE_REQUIRES_CORRECT_CONSTRUCTOR_ARGUMENT_TYPES
Description
The type-inferred constructor of a generic type must use the correct type parameters in the
correct order. For a generic type declared as of type (S, T), the parameterized
constructor must accept arguments (arg0 as S, arg1 as T) in that exact order.
Using the wrong type parameter (e.g., T instead of S), using non-existent
type parameters, or placing them in the wrong order will prevent proper type inference.
Example (Error)
#!ek9
defines module bad.genericnotparameterised.example
defines class
C8 of type (S, T)
default C8()
default C8()
->
//Wrong: both parameters are T, should be (S, T)
@Error: SYMBOL_DEFINITION: GENERIC_TYPE_REQUIRES_CORRECT_CONSTRUCTOR_ARGUMENT_TYPES
arg0 as T
arg1 as T
check()
-> arg0 as T
<- rtn as Boolean: arg0?
Solution
#!ek9
defines module bad.genericnotparameterised.example
defines class
C8Fixed of type (S, T)
default C8Fixed()
default C8Fixed()
->
arg0 as S //Correct: first parameter uses S
arg1 as T //Correct: second parameter uses T
check()
-> arg0 as T
<- rtn as Boolean: arg0?
See Also
E06060: Generic Constructors Must Be Public
Classification: GENERIC_CONSTRUCTORS_MUST_BE_PUBLIC
Description
Generic/template types do not support private or protected constructors. All constructors in generic types must be public (the default visibility) to allow proper parameterization and instantiation. This restriction ensures that generic types can be properly instantiated with concrete type parameters from any context.
Example (Error)
#!ek9
defines module bad.genericnotparameterised.example
defines class
-
While both constructors are defined they are not public, so that's an error.
-?>
C4 of type T
@Error: SYMBOL_DEFINITION: GENERIC_CONSTRUCTORS_MUST_BE_PUBLIC
default private C4()
@Error: SYMBOL_DEFINITION: GENERIC_CONSTRUCTORS_MUST_BE_PUBLIC
default private C4()
-> arg0 as T
check()
-> arg0 as T
<- rtn as Boolean: arg0?
Solution
#!ek9
defines module bad.genericnotparameterised.example
defines class
C4Fixed of type T
//Public by default (no access modifier)
default C4Fixed()
default C4Fixed()
-> arg0 as T
check()
-> arg0 as T
<- rtn as Boolean: arg0?
See Also
E06070: Type Inference Not Supported
Classification: TYPE_INFERENCE_NOT_SUPPORTED
Description
Type inference using the <- operator is not supported within generic/template
type or function definitions. This restriction applies to all variable declarations inside
generic contexts, regardless of whether they use the generic type parameter (T) or concrete
types like Integer. All types must be explicitly declared using as TypeName syntax
to ensure type safety and clarity within generic code.
Example (Error)
#!ek9
defines module bad.inference.example
defines class
AGenericClass of type T
item as T?
default AGenericClass()
AGenericClass()
-> arg0 as T
item :=: arg0
//No inference allowed at all not just with conceptual T
notOkToUse()
<- rtn as Iterator of Integer?
@Error: SYMBOL_DEFINITION: TYPE_INFERENCE_NOT_SUPPORTED
value <- 1
@Error: SYMBOL_DEFINITION: TYPE_INFERENCE_NOT_SUPPORTED
someVar <- Iterator(value)
rtn: someVar
Solution
#!ek9
defines module bad.inference.example
defines class
AGenericClass of type T
item as T?
default AGenericClass()
AGenericClass()
-> arg0 as T
item :=: arg0
okToUse()
<- rtn as Iterator of Integer?
value as Integer: 1 //Explicit type declaration
someVar as Iterator of Integer: Iterator(value)
rtn: someVar
See Also
E06080: Constrained Functions Not Supported
Classification: CONSTRAINED_FUNCTIONS_NOT_SUPPORTED
Description
When constraining a generic type parameter using the constrain by clause,
the constraining type must be a class, trait, component, record, or type. Functions,
text constructs, and constants cannot be used as type constraints because they do not
define structural requirements that can be verified at the type level.
Example (Error)
#!ek9
defines module bad.generic.constraining.types
defines function
CheckFunction()
-> arg0 as String
<- rtn <- true
AbstractFunction() as abstract
-> arg0 as String
<- rtn as Boolean?
defines class
-
Check that it is not possible to constrain by a function.
-?>
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: CONSTRAINED_FUNCTIONS_NOT_SUPPORTED
BadGeneric1 of type T constrain by CheckFunction
default BadGeneric1()
BadGeneric1()
-> arg0 as T
assert arg0?
check()
-> arg0 as T
<- rtn as Boolean: true
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: CONSTRAINED_FUNCTIONS_NOT_SUPPORTED
BadGeneric2 of type T constrain by AbstractFunction
default BadGeneric2()
BadGeneric2()
-> arg0 as T
assert arg0?
check()
-> arg0 as T
<- rtn as Boolean: true
Solution
#!ek9
defines module bad.generic.constraining.types
defines trait
T1
check()
-> arg0 as String
<- rtn <- true
defines component
Comp1 as open
check()
-> arg0 as String
<- rtn <- true
defines class
-
Check it is possible to constrain by a trait
-?>
OKGeneric1 of type T constrain by T1
default OKGeneric1()
OKGeneric1()
-> arg0 as T
assert arg0?
check()
-> arg0 as T
<- rtn as Boolean: true
-
Check it is possible to constrain by a component
-?>
OKGeneric2 of type T constrain by Comp1
default OKGeneric2()
OKGeneric2()
-> arg0 as T
assert arg0?
check()
-> arg0 as T
<- rtn as Boolean: true
See Also
E06090: Generic With Named Dynamic Class
Classification: GENERIC_WITH_NAMED_DYNAMIC_CLASS
Description
Named dynamic classes cannot be used within generic type or function definitions. EK9 prohibits naming dynamic classes in generic contexts to avoid naming conflicts and complexity issues that would arise during generic type instantiation. Dynamic classes within generics must be anonymous (unnamed) to ensure each parameterized instance maintains independent class definitions.
Example (Error)
#!ek9
defines module fuzz.dynamic.generic.type.named
defines trait
T1
method1()
-> arg0 as String
<- rtn as String: arg0
defines class
GenericContainer of type T
process()
-> item as T
//This is INVALID - named dynamic class inside generic type
@Error: SYMBOL_DEFINITION: GENERIC_WITH_NAMED_DYNAMIC_CLASS
dynamic as T1 := NamedDynamic() with trait of T1 as class
override method1()
-> arg0 as String
<- rtn as String: "processed: " + arg0
assert dynamic?
Solution
#!ek9
defines module fuzz.dynamic.generic.type.named
defines trait
T1
method1()
-> arg0 as String
<- rtn as String: arg0
defines class
GenericContainer of type T
process()
-> item as T
//Anonymous dynamic class (no name) - this is allowed
dynamic as T1 := () with trait of T1 as class
override method1()
-> arg0 as String
<- rtn as String: "processed: " + arg0
assert dynamic?
See Also
E06100: Generic Function Implementation Required
Classification: GENERIC_FUNCTION_IMPLEMENTATION_REQUIRED
Description
When creating a dynamic function from an abstract generic function, an implementation body
must be provided. Abstract generic functions cannot be used directly without implementing
their abstract behavior. This error occurs when attempting to create a dynamic function
instance using is AbstractGenericFunction as function syntax without providing
the required method implementation.
Example (Error)
#!ek9
defines module generic.function.declaration.only
defines function
//Define an abstract generic function with type parameter T
AbstractProcessor() of type T as abstract
-> input as T
<- result as T?
defines program
TestInvalidGenericFunctionUsage()
//Try to use abstract generic function without providing implementation
//This should fail because the function needs an implementation body
@Error: FULL_RESOLUTION: GENERIC_FUNCTION_IMPLEMENTATION_REQUIRED
processor <- () is AbstractProcessor of String as function
assert processor?
Solution
#!ek9
defines module generic.function.declaration.only
defines function
AbstractProcessor() of type T as abstract
-> input as T
<- result as T?
defines program
TestValidGenericFunctionUsage()
//Provide implementation for the abstract generic function
processor <- () is AbstractProcessor of String as function
-> input as String
<- result as String: input + " processed"
assert processor?
See Also
E06110: Constructor With Function In Generic
Classification: CONSTRUCTOR_WITH_FUNCTION_IN_GENERIC
Description
When a generic type is parameterized with a function type, the generic cannot use operations
that assume constructors. Functions in EK9 do not have constructors in the same way classes do
(using T() syntax). This error occurs when a generic type attempts to call
T() assuming a constructor, but T is bound to a function type.
Generic types can contain functions (as with Optional or List), but
cannot invoke function constructors.
Example (Error)
#!ek9
defines module bad.generic.constraining.resolution2
defines function
CheckFunction()
-> arg0 as Integer
<- rtn as Boolean: arg0 > 21
defines class
UnconstrainedGeneric1 of type T
default UnconstrainedGeneric1()
default UnconstrainedGeneric1()
-> arg0 as T
check()
->
arg0 as T
arg1 as T
<-
rtn as Integer: arg0 <=> arg1
createNew()
<- rtn as T: T() //Assumes constructor exists
defines function
//This generic type assumes the possibility of a constructor
ExampleOfInvalidTypeUse4()
@Error: POST_RESOLUTION_CHECKS: CONSTRUCTOR_WITH_FUNCTION_IN_GENERIC
demo <- UnconstrainedGeneric1() of CheckFunction
result <- demo.check(CheckFunction, CheckFunction)
assert result == 0
Solution
#!ek9
defines module bad.generic.constraining.resolution2
defines function
CheckFunction()
-> arg0 as Integer
<- rtn as Boolean: arg0 > 21
defines class
//This generic type can work with functions because it only uses '?'
UnconstrainedGeneric2 of type T
default UnconstrainedGeneric2()
default UnconstrainedGeneric2()
-> arg0 as T
check()
->
arg0 as T
arg1 as T
<-
rtn as Boolean: arg0? and arg1? //Only uses is-set operator
defines function
ExampleOfValidTypeUse5()
demo <- UnconstrainedGeneric2() of CheckFunction
result <- demo.check(CheckFunction, CheckFunction)
assert result
See Also
E06120: Function Used In Generic
Classification: FUNCTION_USED_IN_GENERIC
Description
When a generic type is parameterized with a function type, the generic cannot use operators
that are not applicable to functions. Functions in EK9 support only the is-set operator
(?) and cannot be used with operators like addition (+), comparison
(<=>), or other operators that assume non-function types. Generic types can
contain functions, but only if they restrict operations to those supported by function types.
Example (Error)
#!ek9
defines module bad.generic.constraining.resolution3
defines function
AbstractFunction() as abstract
-> arg0 as String
<- rtn as Boolean?
//Unconstrained generic function that uses the addition operator
GF1 of type T
->
arg0 as T
arg1 as T
<-
rtn as T: arg0 + arg1
defines function
//This generic assumes '+' operator, but functions don't have '+'
GF1CannotBeUsedWithFunctions()
@Error: POST_RESOLUTION_CHECKS: FUNCTION_USED_IN_GENERIC
gf1 <- GF1() of AbstractFunction
assert gf1?
Solution
#!ek9
defines module bad.generic.constraining.resolution3
defines function
AbstractFunction() as abstract
-> arg0 as String
<- rtn as Boolean?
ConcreteFunction is AbstractFunction
-> arg0 as String
<- rtn <- true
//Generic function using only is-set operator (works with functions)
GF3 of type T
->
arg0 as T
arg1 as T
<- rtn as T: arg0? <- arg0 : arg1
defines function
GF3CanBeUsedWithFunctions()
-> possiblyUnSetFunction as AbstractFunction
gf3 <- GF3() of AbstractFunction
//Use a normal function and a dynamic function
func <- gf3(possiblyUnSetFunction, () is AbstractFunction (rtn: false))
assert func?
See Also
E06130: Constrained Type Constructor Missing
Classification: CONSTRAINED_TYPE_CONSTRUCTOR_MISSING
Description
When a generic type or function is constrained using constrain by, the type used
to parameterize the generic must provide all constructors that exist on the constraining type.
EK9 enforces constructor completeness to ensure that generic code can safely instantiate new
instances using T(constrainingTypeConstructorSignature). If the parameterizing type
is missing any constructor that the constraining type defines, this error is raised.
Example (Error)
#!ek9
defines module bad.generic.constraining.resolution3
defines record
R2 as open
value as Integer?
R2() as pure
value :=? 1
R2() as pure
-> arg0 as Integer
value :=? Integer(arg0)
R2() as pure
-> arg0 as R2
value :=? Integer(arg0.value)
operator + as pure
-> arg0 as R2
<- rtn as R2: R2(value + arg0.value)
default operator ?
R3 is R2
anotherValue as Integer?
R3() as pure
value :=? 1
R3() as pure
-> arg0 as Integer
super(arg0)
//Missing: R3(-> arg0 as R2) constructor
defines function
GF2 of type T constrain by R2
->
arg0 as T
arg1 as T
<-
rtn as T: T(arg0 + arg1) //Requires constructor T(-> arg0 as R2)
defines function
WithAdditionOperatorAndR3()
//R3 is missing the R3(-> arg0 as R2) constructor required by constraint
@Error: POST_RESOLUTION_CHECKS: CONSTRAINED_TYPE_CONSTRUCTOR_MISSING
gf2 <- GF2() of R3
assert gf2?
Solution
#!ek9
defines module bad.generic.constraining.resolution3
defines record
R2 as open
value as Integer?
R2() as pure
value :=? 1
R2() as pure
-> arg0 as Integer
value :=? Integer(arg0)
R2() as pure
-> arg0 as R2
value :=? Integer(arg0.value)
operator + as pure
-> arg0 as R2
<- rtn as R2: R2(value + arg0.value)
default operator ?
R4 is R2
anotherValue as Integer?
R4() as pure
value :=? 1
R4() as pure
-> arg0 as Integer
super(arg0)
R4() as pure
-> arg0 as R2 //Now includes required constructor
super(arg0)
defines function
GF2 of type T constrain by R2
->
arg0 as T
arg1 as T
<-
rtn as T: T(arg0 + arg1)
defines function
WithAdditionOperatorAndR4()
gf2 <- GF2() of R4 //R4 has all required constructors
assert gf2?
See Also
E06140: Method Ambiguous
Classification: METHOD_AMBIGUOUS
Description
Multiple overloaded methods match the call signature with equal cost, making it impossible for the compiler to determine which method to invoke. EK9 uses a cost-based method resolution algorithm where each type conversion/coercion has an associated cost. When two or more methods have matching costs within a tolerance of 0.001, the call is ambiguous. This can occur when combining superclass inheritance and trait implementation, where the type hierarchy distances result in equal matching costs.
Example (Error)
#!ek9
defines module fuzztest.ambiguity.superclass.trait.equal
defines trait
T
getValue() as abstract
<- rtn as Integer?
defines class
A as open
default A as pure
B extends A as open
default B as pure
C extends B with trait of T
default C as pure
override getValue()
<- rtn <- 42
defines class
TestClass
checkMethod()
-> arg as A
<- rtn <- 10
checkMethod()
-> arg as T
<- rtn <- 20
default TestClass as pure
defines program
TestAmbiguity()
c <- C()
test <- TestClass()
//AMBIGUOUS: C→B→A (0.05+0.05=0.10) vs C→T (0.10)
@Error: FULL_RESOLUTION: METHOD_AMBIGUOUS
test.checkMethod(c)
Solution: Make Match Costs Distinct
#!ek9
defines module fuzztest.ambiguity.superclass.trait.equal
defines trait
T
getValue() as abstract
<- rtn as Integer?
defines class
A as open
default A as pure
B extends A as open
default B as pure
C extends B with trait of T
default C as pure
override getValue()
<- rtn <- 42
defines class
TestClass
checkMethod()
-> arg as B //Use B (0.05) instead of A (0.10)
<- rtn <- 10
checkMethod()
-> arg as T //Cost 0.10
<- rtn <- 20
default TestClass as pure
defines program
TestAmbiguity()
c <- C()
test <- TestClass()
test.checkMethod(c) //Unambiguous: C→B (0.05) < C→T (0.10)
See Also
E06150: Methods Conflict
Classification: METHODS_CONFLICT
Description
When a class implements multiple traits or extends a base class while implementing traits, method conflicts can occur if multiple sources provide concrete (non-abstract) implementations of the same method. EK9 requires explicit resolution by overriding the conflicting method to provide a clear, single implementation. This prevents the diamond problem and ensures unambiguous method resolution.
Example (Error)
#!ek9
defines module clashing.implementations
defines trait
T1
clash()
<- rtn <- true
T2
clash()
<- rtn <- false
defines class
Base as open
clash()
<- rtn <- false
default operator ?
//ERROR: Two traits both provide concrete clash() implementations
@Error: FULL_RESOLUTION: METHODS_CONFLICT
BrokenClass1 with trait T1, T2
default BrokenClass1()
someMethod()
<- rtn <- true
default operator ?
//ERROR: Base class and trait both provide concrete clash()
@Error: FULL_RESOLUTION: METHODS_CONFLICT
BrokenClass2 extends Base with trait T1
default BrokenClass2()
someMethod()
<- rtn <- true
default operator ?
Solution
#!ek9
defines module clashing.implementations
defines trait
T1
clash()
<- rtn <- true
T2
clash()
<- rtn <- false
defines class
Base as open
clash()
<- rtn <- false
default operator ?
//Provide explicit override to resolve conflict
WorkingClass1 with trait T1, T2
default WorkingClass1()
override clash()
<- rtn <- true //Explicit resolution
default operator ?
WorkingClass2 is Base with trait T1
default WorkingClass2()
override clash()
<- rtn <- true //Explicit resolution
default operator ?
See Also
E06160: Not Immediate Trait
Classification: NOT_IMMEDIATE_TRAIT
Description
EK9 restricts direct trait method calls to only those traits explicitly and immediately declared on the current class or dynamic class. You cannot call methods from traits inherited through a trait hierarchy (super-traits) or from traits on a parent class. This restriction prevents tight coupling to implementation details of inherited trait hierarchies, making code easier to refactor. Only explicitly declared immediate traits are accessible via direct trait method calls.
Example (Error)
#!ek9
defines module bad.directtraitcalls
defines trait
T2
simpleMessage() as pure
<- rtn as String: "T2"
T1 extends T2
anotherMessage() as pure
<- rtn as String: "T1"
defines class
//Class explicitly declares T1, which extends T2
inValidClassViaSuperTrait with trait of T1
someMethod()
//T2 is NOT immediate (it's a super-trait of T1)
@Error: FULL_RESOLUTION: NOT_IMMEDIATE_TRAIT
var1 <- T2.simpleMessage()
assert var1?
//T1 IS immediate, so this is allowed
var2 <- T1.anotherMessage()
assert var2?
InvalidT2AccessFromClass
someMethod()
//T2 is not declared on this class at all
@Error: FULL_RESOLUTION: NOT_IMMEDIATE_TRAIT
var <- T2.simpleMessage()
assert var?
Solution
#!ek9
defines module bad.directtraitcalls
defines trait
T2
simpleMessage() as pure
<- rtn as String: "T2"
T1 extends T2
anotherMessage() as pure
<- rtn as String: "T1"
defines class
//Explicitly declare T2 as immediate trait
ValidClass1 with trait of T2
someMethod()
var <- T2.simpleMessage() //Now allowed
assert var?
//Explicitly declare both T1 and T2 if you need both
ValidClass2 with trait of T1, T2
someMethod()
var1 <- T1.anotherMessage() //Allowed
var2 <- T2.simpleMessage() //Allowed
assert var1? and var2?
See Also
E06170: Trait Access Not Supported
Classification: TRAIT_ACCESS_NOT_SUPPORTED
Description
Direct trait method calls (e.g., TraitName.methodName()) are only supported within
classes or dynamic classes that explicitly declare that trait. Functions, dynamic functions created
within functions or classes without the trait, and other contexts cannot access trait methods directly.
Trait method access is a special syntax restricted to contexts where the trait is part of the type's
explicit contract.
Example (Error)
#!ek9
defines module bad.directtraitcalls
defines trait
T2
simpleMessage() as pure
<- rtn as String: "T2"
defines function
//Trait method call from standalone function - not allowed
SomeFunction()
@Error: FULL_RESOLUTION: TRAIT_ACCESS_NOT_SUPPORTED
var <- T2.simpleMessage()
assert var?
//Dynamic function trying to access trait - not allowed
TestDynamicFunction()
var <- String()
@Error: FULL_RESOLUTION: TRAIT_ACCESS_NOT_SUPPORTED
someFunction <- () is Acceptor of String as function ( t :=: T2.simpleMessage() )
someFunction(var)
defines class
//Even in a class with the trait, dynamic functions can't access it
InvalidT2AccessFromDynamicFunctionInClass with trait of T2
NotAllowedFromDynamicClass()
var <- String()
@Error: FULL_RESOLUTION: TRAIT_ACCESS_NOT_SUPPORTED
someFunction <- () is Acceptor of String as function ( t :=: T2.simpleMessage() )
someFunction(var)
Solution
#!ek9
defines module bad.directtraitcalls
defines trait
T2
simpleMessage() as pure
<- rtn as String: "T2"
LenValidator
validateLength() abstract
-> p as String
<- r as Boolean?
defines function
//Dynamic class explicitly declares traits - trait access allowed
TestValidDynamicClass()
lenValidator <- () trait of LenValidator, T2 as class
override validateLength()
-> p as String
<- r as Boolean: false
if p?
message <- `${T2.simpleMessage()}: ${p}` //Allowed
r := length message < 300
result <- lenValidator.validateLength("Steve")
assert result?
See Also
E06180: Not Accessible
Classification: NOT_ACCESSIBLE
Description
A method, field, constructor, or other member is not accessible from the current context due to
visibility modifiers. EK9 enforces strict access control: private members are only
accessible within the defining class, protected members are accessible within the
defining class and its subclasses, and public members (default) are accessible everywhere. This
error occurs when attempting to access private members from outside the class, protected members
from unrelated classes, or private constructors from subclasses.
Example (Error)
#!ek9
defines module bad.classmethod.access3
defines class
C1 as open
default private C1()
C1()
-> arg0 as String
//Public constructor
private privMethod()
<- rtn <- true
protected protMethod()
<- rtn <- true
C2 extends C1
C2()
//Cannot call private constructor from subclass
@Error: FULL_RESOLUTION: NOT_ACCESSIBLE
super()
checkAccessToPrivateMethodInSuper()
//Cannot access private method from subclass
@Error: FULL_RESOLUTION: NOT_ACCESSIBLE
someValueFromPrivateMethod <- privMethod()
assert someValueFromPrivateMethod?
UnrelatedToC1
default UnrelatedToC1()
testProtectedAccessViaC2()
-> arg0 as C2
//Cannot access protected method from unrelated class
@Error: FULL_RESOLUTION: NOT_ACCESSIBLE
result1A <- arg0.protMethod()
assert result1A?
testPrivateAccessViaC2()
-> arg0 as C2
//Cannot access private method from unrelated class
@Error: FULL_RESOLUTION: NOT_ACCESSIBLE
result1A <- arg0.privMethod()
assert result1A?
Solution
#!ek9
defines module bad.classmethod.access3
defines class
C1 as open
default C1() //Make constructor public
C1()
-> arg0 as String
//Make methods public for external access
privMethod()
<- rtn <- true
protMethod()
<- rtn <- true
C2 extends C1
C2()
super() //Now accessible
checkAccessToPrivateMethodInSuper()
someValueFromPrivateMethod <- privMethod() //Now accessible
assert someValueFromPrivateMethod?
UnrelatedToC1
default UnrelatedToC1()
testMethodAccessViaC2()
-> arg0 as C2
result1A <- arg0.protMethod() //Now accessible
result1B <- arg0.privMethod() //Now accessible
assert result1A? and result1B?
See Also
E06190: Result Must Have Different Types
Classification: RESULT_MUST_HAVE_DIFFERENT_TYPES
Description
The Result type is designed to distinguish between success and failure scenarios.
It requires two different types: one for the success value (OK) and another for the error value.
Using the same type for both parameters defeats the entire purpose of Result, as there would be no way
to distinguish between successful results and error conditions.
Example (Error)
#!ek9 - RESULT_MUST_HAVE_DIFFERENT_TYPES: Result with Integer for both OK and Error types. Tests that Resultis not allowed. -?> defines module fuzztest.typeconstraint.result.integer defines function invalidResultInteger() as abstract @Error: SYMBOL_DEFINITION: RESULT_MUST_HAVE_DIFFERENT_TYPES <- rtn as Result of (Integer, Integer)? //EOF
Solution
#!ek9
defines module fuzztest.typeconstraint.result.integer
defines function
validResultInteger() as abstract
<- rtn as Result of (Integer, Exception)? //Different types
See Also
E06200: Parenthesis Not Required
Classification: PARENTHESIS_NOT_REQUIRED
Description
Parentheses () should not be used in this context. This error occurs when parentheses
appear where they don't belong, particularly in generic type declarations. In EK9, parentheses
indicate construction (creating a new instance), while of indicates
type parameterization. These are distinct concepts and should not be mixed.
Common cases include: List() of String (incorrect), GenericClass() of Type (wrong),
or val as List() of String (invalid declaration syntax).
Example (Error)
#!ek9
-
Generics incorrect use - that can be detected in phase1 def phase.
-?>
defines module incorrect.generic.uses
defines function
@Resolved: SYMBOL_DEFINITION: FUNCTION: "invalidPhase1IncorrectParenthesis1"
invalidPhase1IncorrectParenthesis1()
//Failure1
//Use of parenthesis on lhs
@Error: SYMBOL_DEFINITION: PARENTHESIS_NOT_REQUIRED
val1 as List() of String?
//Failure2
//Then missing parenthesis
@Error: SYMBOL_DEFINITION: PARENTHESIS_REQUIRED
val2 as List of String: List of String
//Failure3
//Again missing parenthesis
@Error: SYMBOL_DEFINITION: PARENTHESIS_REQUIRED
val3 <- List of String
//Failure4 - should be List() of List of String
@Error: SYMBOL_DEFINITION: PARENTHESIS_NOT_REQUIRED
val4 <- List() of List() of String
//EOF
Solution
#!ek9
defines module incorrect.generic.uses
defines function
validGenericSyntax()
//Correct: () for construction, 'of Type' for parameterization
val1 as List of String: List()
//Correct: Construct and parameterize together
val2 <- List() of String
//Correct: Nested generics - only outermost gets ()
val3 <- List() of List of String
See Also
E06210: Parenthesis Required
Classification: PARENTHESIS_REQUIRED
Description
Parentheses () are required but were omitted. This error occurs when attempting
to use a type name or construct an instance without the required parentheses. In EK9, parentheses
indicate construction - the creation of a new instance. Without parentheses,
the compiler sees only a type reference, not an instance creation.
Common cases include: constructing objects on the right-hand side (List of String should be
List() of String), or when a statement is meant to create an instance but lacks the
construction operator.
Example (Error)
#!ek9
-
Generics incorrect use - demonstrates missing parentheses
-?>
defines module incorrect.generic.uses
defines function
@Resolved: SYMBOL_DEFINITION: FUNCTION: "invalidPhase1IncorrectParenthesis1"
invalidPhase1IncorrectParenthesis1()
//Failure2 - missing () on rhs
@Error: SYMBOL_DEFINITION: PARENTHESIS_REQUIRED
val2 as List of String: List of String
//Failure3 - missing () when constructing
@Error: SYMBOL_DEFINITION: PARENTHESIS_REQUIRED
val3 <- List of String
//Failure5 - not allowed - as does not create a new 'List' as missing ()
@Error: SYMBOL_DEFINITION: PARENTHESIS_REQUIRED
List of String
//EOF
Solution
#!ek9
defines module incorrect.generic.uses
defines function
validConstructionSyntax()
//Correct: () indicates construction
val2 as List of String: List()
//Correct: Construct with ()
val3 <- List() of String
//Correct: Create instance (has side effects)
List() of String
See Also
E06220: Values And Type Incompatible
Classification: VALUES_AND_TYPE_INCOMPATIBLE
Description
EK9 supports type inference when constructor arguments are provided, OR explicit
type parameterization with of Type, but not both simultaneously.
This error occurs when you attempt to mix explicit type parameters with constructor values, which creates
ambiguity - the compiler cannot determine whether to infer the type from the values or use the explicit
type parameters.
Use GenericThing(32) for type inference, OR GenericThing() of Integer for
explicit typing, but never GenericThing(32) of Integer.
Example (Error)
#!ek9
-
Demonstrates mixing type inference with explicit type parameters
-?>
defines module incorrect.generic.uses
defines class
GenericThing of type T
item as T?
default GenericThing()
GenericThing()
-> arg as T
this.item = arg
defines function
@Resolved: SYMBOL_DEFINITION: FUNCTION: "invalidMixOfInferenceAndExplicit"
invalidMixOfInferenceAndExplicit()
validInferenceCombination1 <- GenericThing() of Integer
validInferenceCombination2 <- GenericThing(32)
//Failure 13 - Not allowed because if parameters are provided in construction then we infer type.
@Error: SYMBOL_DEFINITION: VALUES_AND_TYPE_INCOMPATIBLE
invalidInferenceCombination <- GenericThing(32) of Integer
//EOF
Solution 1: Use Type Inference
#!ek9
defines module incorrect.generic.uses
defines function
useTypeInference()
//Type is inferred from constructor argument (32 → Integer)
thing <- GenericThing(32)
Solution 2: Use Explicit Type
#!ek9
defines module incorrect.generic.uses
defines function
useExplicitType()
//Type is explicitly declared
thing <- GenericThing() of Integer
See Also
- Generic Type Inference
- Generic Type Parameterization
E06230: Captured Variable Must Be Named
Classification: CAPTURED_VARIABLE_MUST_BE_NAMED
Description
When capturing values in dynamic classes or functions, EK9 distinguishes between simple
identifiers (variables) and expressions (calculations, function calls, literals).
Simple identifiers can be captured directly: (varName). However, expressions MUST use
named parameter syntax: (name: expression). This requirement ensures
clarity and prevents confusion about what values are being captured.
Common violations include: (getValue()) (function call without name), (100)
(literal without name), or (10 + 20) (arithmetic without name). All must be written as
(value: getValue()), (threshold: 100), etc.
Example (Error)
#!ek9
-
Dynamic Class/Function Fuzz Test: Capturing Expression Without Name
Tests: CAPTURED_VARIABLE_MUST_BE_NAMED
Description: When capturing an expression (not a simple identifier), the capture
MUST use named parameter syntax. This test verifies that function calls, literals,
and complex expressions require explicit names.
-?>
defines module fuzz.dynamic.capture.expression.unnamed
defines function
getValue() as pure
<- rtn as Integer: 42
discriminator() as abstract
-> s as String
<- rtn as Boolean?
defines program
TestCaptureExpressionWithoutName()
//INVALID - function call without name
@Error: SYMBOL_DEFINITION: CAPTURED_VARIABLE_MUST_BE_NAMED
fn1 <- (getValue()) is discriminator as function
rtn: length s > min
//INVALID - literal without name
@Error: SYMBOL_DEFINITION: CAPTURED_VARIABLE_MUST_BE_NAMED
fn2 <- (100) is discriminator as function
rtn: length s < max
//INVALID - arithmetic expression without name
@Error: SYMBOL_DEFINITION: CAPTURED_VARIABLE_MUST_BE_NAMED
fn3 <- (10 + 20) is discriminator as function
rtn: length s > threshold
//EOF
Solution
#!ek9
defines module fuzz.dynamic.capture.expression.unnamed
defines program
TestCaptureExpressionWithName()
//VALID - function call WITH name
fn1 <- (min: getValue()) is discriminator as function
rtn: length s > min
//VALID - literal WITH name
fn2 <- (max: 100) is discriminator as function
rtn: length s < max
//VALID - expression WITH name
fn3 <- (threshold: 10 + 20) is discriminator as function
rtn: length s > threshold
See Also
E06240: Either All Parameters Named Or None
Classification: EITHER_ALL_PARAMETERS_NAMED_OR_NONE
Description
EK9 enforces consistency in parameter naming - either all parameters must be named or none can be named. Mixing named and unnamed (positional) parameters is prohibited to maintain code clarity and prevent confusion about parameter order and intent.
This rule applies to dynamic class/function captures, constructor calls, and method/function invocations.
Valid: (min: 10, max: 100) (all named) or (10, 100) (all positional).
Invalid: (min: 10, 100) (mixing named and positional).
Example (Error)
#!ek9
-
Dynamic Class/Function Fuzz Test: Mixed Named and Unnamed Parameters
Tests: EITHER_ALL_PARAMETERS_NAMED_OR_NONE
Description: EK9 enforces consistency - either ALL capture parameters must be
named, or NONE can be named. Mixing named and unnamed parameters is prohibited
to maintain code clarity and prevent confusion.
-?>
defines module fuzz.dynamic.capture.mixed.naming
defines trait
Comparator
compare()
-> value as String
<- result as Boolean?
defines program
TestMixedNaming()
min <- 10
max <- 100
//INVALID - mixing named (min:) and unnamed (max) parameters
@Error: SYMBOL_DEFINITION: EITHER_ALL_PARAMETERS_NAMED_OR_NONE
comparator <- (min: 10, max) with trait of Comparator as class
override compare()
-> value as String
<- result as Boolean: length value > min and length value < max
assert comparator?
//EOF
Solution 1: All Named
#!ek9
defines module fuzz.dynamic.capture.mixed.naming
defines program
TestAllNamed()
min <- 10
max <- 100
//VALID - all parameters named
comparator <- (min: 10, max: 100) with trait of Comparator as class
override compare()
-> value as String
<- result as Boolean: length value > min and length value < max
Solution 2: All Positional
#!ek9
defines module fuzz.dynamic.capture.mixed.naming
defines program
TestAllPositional()
min <- 10
max <- 100
//VALID - all parameters positional (simple identifiers)
comparator <- (min, max) with trait of Comparator as class
override compare()
-> value as String
<- result as Boolean: length value > min and length value < max
See Also
E06250: Named Parameters Must Match Arguments
Classification: NAMED_PARAMETERS_MUST_MATCH_ARGUMENTS
Description
When using named parameters, the parameter names MUST exactly match the declared argument names of the function, method, or constructor being called. Named parameters provide self-documenting code and prevent argument order errors, but the compiler strictly enforces name matching to ensure correctness.
Common violations: using badName: value when parameter is arg0,
or swapping names like arg1: stringValue, arg0: intValue when types don't match positions.
Example (Error)
#!ek9
defines module bad.named.arguments.examples
defines class
C1
aMethod()
->
arg0 as String
arg1 as Float
assert arg0? and arg1?
defines function
showInvalidChainedMethodNamedAccess1()
c2 <- C2()
c2.c1().aMethod(
@Error: FULL_RESOLUTION: NAMED_PARAMETERS_MUST_MATCH_ARGUMENTS
badName1: "Steve",
@Error: FULL_RESOLUTION: NAMED_PARAMETERS_MUST_MATCH_ARGUMENTS
alsoABadName: 1
)
showInvalidChainedMethodNamedAccess2()
c2 <- C2()
c2.c1().aMethod(
@Error: FULL_RESOLUTION: NAMED_PARAMETERS_MUST_MATCH_ARGUMENTS
arg1: "Steve",
@Error: FULL_RESOLUTION: NAMED_PARAMETERS_MUST_MATCH_ARGUMENTS
arg0: 1
)
//EOF
Solution
#!ek9
defines module bad.named.arguments.examples
defines function
showValidChainedMethodNamedAccess()
c2 <- C2()
//Correct: parameter names match exactly
c2.c1().aMethod(arg0: "Steve", arg1: 1)
See Also
E06260: Parameter Mismatch
Classification: PARAMETER_MISMATCH
Description
The argument types provided don't match the expected parameter types defined in the function, method, or constructor signature. This is a type compatibility error detected during method/function call resolution.
Example (Error)
#!ek9
-
Tests PARAMETER_MISMATCH error.
Function call with wrong parameter type (Integer instead of String).
-?>
defines module bad.parameter.mismatch
defines function
acceptsString()
-> param as String
<- rtn as String: param
testParameterMismatch()
<- rtn as String: acceptsString(123) //Passing Integer instead of String - should trigger PARAMETER_MISMATCH
//EOF
Solution
#!ek9
defines module bad.parameter.mismatch
defines function
testParameterMismatch()
<- rtn as String: acceptsString("123") //Correct: String parameter
See Also
E06270: Function Parameter Mismatch
Classification: FUNCTION_PARAMETER_MISMATCH
Description
Function delegate parameters don't match the expected signature. This error occurs when calling a
function delegate variable with arguments that don't match the delegate's declared parameter types.
Common scenario: attempting to call a no-parameter function delegate (() <- Integer)
with arguments, or vice versa.
Example (Error)
#!ek9
defines module bad.detailed.resolution
defines function
SomeFunction() as abstract
<- rtn as Integer?
defines class
C3A
//Now its not just a variable but a delegate
method2 as SomeFunction?
C3A()
-> arg0 as SomeFunction
this.method2: arg0
method1()
<- rtn as Boolean?
//Now it really is a function delegate, but we pass the wrong parameters (not also has the wrong return type - see next test)
@Error: FULL_RESOLUTION: FUNCTION_PARAMETER_MISMATCH
rtn: method2(21)
//EOF
Solution
#!ek9
defines module bad.detailed.resolution
defines class
C6A
method2 as SomeFunction?
C6A()
-> arg0 as SomeFunction
this.method2: arg0
method1()
<- rtn as Integer?
//Finally this will be OK - no parameters, correct return type
rtn: method2()
See Also
E06280: Too Many Arguments
Classification: TOO_MANY_ARGUMENTS
Description
More arguments were provided than the operator/method/function accepts. This is detected during operator definition validation - each EK9 operator has a specific parameter count requirement, and providing extra parameters violates the operator contract.
Example (Error)
#!ek9
defines module fuzztest.operators.test018
defines class
Number
value <- 0
// Should fail - + operator requires exactly 1 parameter, not 2
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: TOO_MANY_ARGUMENTS
operator + as pure
->
a as Number
b as Number
<- result as Number: Number()
//EOF
Solution
#!ek9
defines module fuzztest.operators.test018
defines class
Number
value <- 0
// Correct - + operator requires exactly 1 parameter
operator + as pure
-> arg as Number
<- result as Number: Number()
See Also
E06290: Too Few Arguments
Classification: TOO_FEW_ARGUMENTS
Description
Fewer arguments were provided than the operator/method/function requires. This is detected during operator definition validation - each EK9 operator has specific parameter count requirements, and omitting required parameters violates the operator contract.
Example (Error)
#!ek9
defines module fuzztest.operators.test019
defines class
Value
amount <- 0
//Need a pure default constructor for the addition return
default Value() as pure
// Should fail - + operator requires 1 parameter, given 0
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: TOO_FEW_ARGUMENTS
operator + as pure
<- result as Value: Value()
//EOF
Solution
#!ek9
defines module fuzztest.operators.test019
defines class
Value
amount <- 0
default Value() as pure
// Correct - + operator requires exactly 1 parameter
operator + as pure
-> arg as Value
<- result as Value: Value()
See Also
E06300: Require One Argument
Classification: REQUIRE_ONE_ARGUMENT
Description
This stream operation requires exactly one argument but received zero or multiple arguments.
Stream pipeline operators like map require a single function parameter to transform
each element in the stream.
Example (Error)
#!ek9
-
Examples of some working and some none working map functions in a Stream.
-?>
defines module bad.streams3
defines class
StringCollector
joined <- String()
operator |
-> arg0 as String
if arg0?
if joined?
joined += " " + arg0
else
joined: String(arg0)
override operator ? as pure
<- rtn as Boolean: joined?
defines function
IncompatibleFunction1()
->
arg0 as Integer
arg1 as Integer
<-
rtn as String: ""
BrokenStreamCatMap4()
collector <- StringCollector()
@Error: FULL_RESOLUTION: REQUIRE_ONE_ARGUMENT
cat [1, 2, 3] | map with IncompatibleFunction1 > collector
//EOF
Solution
#!ek9
defines module bad.streams3
defines function
IntegerToString()
-> value as Integer
<- rtn as String: value? <- $value else String()
SimpleStreamCatMap1()
collector <- StringCollector()
//Check if Integer can be mapped to a String and then output.
cat [1, 2, 3] | map with IntegerToString > collector
assert collector?
See Also
E06310: Require No Arguments
Classification: REQUIRE_NO_ARGUMENTS
Description
The CALL and ASYNC stream operators require functions with signature
() -> T (no parameters, must return value). This error occurs when attempting to use
these operators with functions that require parameters. The CALL/ASYNC operators invoke each
function in the stream without arguments, making parameterized functions incompatible.
Example (Error)
#!ek9
-
Tests CALL and ASYNC operator errors.
CALL/ASYNC require () → T functions (no parameters, must return value).
Tests function type requirements and execution semantics.
-?>
defines module fuzztest.stream.call.async
defines function
- Valid function for reference -?>
ValidCallable()
<- rtn as Integer: 42
- Helper functions for testing -?>
NeedsParam()
-> n as Integer
<- rtn as Integer: n * 2
CallWithParameterizedFunction()
- CALL on function that requires parameters -?>
@Error: FULL_RESOLUTION: REQUIRE_NO_ARGUMENTS
result <- cat [NeedsParam] | call | collect as List of Integer
assert result?
//EOF
Solution
#!ek9
defines module fuzztest.stream.call.async
defines function
ValidCallable()
<- rtn as Integer: 42
CorrectCallUsage()
//Correct: ValidCallable takes no parameters and returns Integer
result <- cat [ValidCallable] | call | collect as List of Integer
assert result?
See Also
E06320: Invalid Number Of Parameters
Classification: INVALID_NUMBER_OF_PARAMETERS
Description
Dispatcher methods require 1 or 2 parameters, not zero or more than two. Dispatchers in EK9 use dynamic dispatch based on parameter types at runtime. A zero-parameter dispatcher cannot dispatch (no parameters to inspect), and more than two parameters would complicate the dispatch logic beyond the supported patterns.
Example (Error)
#!ek9
defines module fuzztest.dispatcher.purity.variant4
// Variation 4: Dispatcher With Zero Parameters
// Question: Can a dispatcher method have zero parameters?
// Result: CONFIRMED - dispatchers require 1 or 2 parameters
defines class
ZeroParamDispatcher
@Error: FULL_RESOLUTION: INVALID_NUMBER_OF_PARAMETERS
process() as dispatcher
assert true
process()
-> arg as Integer
assert arg?
//EOF
Solution
#!ek9
defines module fuzztest.dispatcher.purity.variant4
defines class
ValidDispatcher
//Correct: dispatcher with 1 parameter
process() as dispatcher
-> arg as Integer
assert arg?
process()
-> arg as String
assert arg?
See Also
E06330: Incompatible Type Arguments
Classification: INCOMPATIBLE_TYPE_ARGUMENTS
Description
Function parameter types don't match the stream pipeline's data type. This error occurs in stream
processing when providing a function to operators like sort that expect specific
argument types matching the stream's element type. For example, sorting a stream of R1
requires a comparator function with signature (R1, R1) -> Integer.
Example (Error)
#!ek9
-
The 'sort' accepts any 'T' and if used without a function expects the 'T' to have a comparator method. If it is used with a function
then that function must accept two arguments of 'T' and return an Integer (i.e. be a comparator.)
-?>
defines module bad.streams6
defines function
InvalidProp1ComparatorC()
->
o1 as Date
o2 as R1
<-
rtn as Integer: 66
InvalidProp1ComparatorE()
->
o1 as Duration
o2 as Duration
<-
rtn as Integer: 1
defines record
R1 as open
prop1 as String: String()
prop2 as Date: Date()
defines function
InvalidComparatorFunctionStreamCatSort3()
collector <- StringCollector()
@Error: FULL_RESOLUTION: INCOMPATIBLE_TYPE_ARGUMENTS
cat [R1("last", 2010-10-01), R1("last", 2010-10-02), R1("first", 2010-10-01)] | sort with InvalidProp1ComparatorC > collector
assert collector?
InvalidComparatorFunctionStreamCatSort5()
collector <- StringCollector()
@Error: FULL_RESOLUTION: INCOMPATIBLE_TYPE_ARGUMENTS
cat [R1("last", 2010-10-01), R1("last", 2010-10-02), R1("first", 2010-10-01)] | sort with InvalidProp1ComparatorE > collector
assert collector?
//EOF
Solution
#!ek9
defines module bad.streams6
defines function
Prop1R1Comparator()
->
o1 as R1
o2 as R1
<-
rtn as Integer: o1.prop1 <=> o2.prop1
ComparatorFunctionStreamCatSort4()
collector <- StringCollector()
//We would expect this to reorder the values and output - this would use the 'Integer <- Prop1R1Comparator(o1, o2)' function.
cat [R1("last", 2010-10-01), R1("last", 2010-10-02), R1("first", 2010-10-01)] | sort with Prop1R1Comparator > collector
assert collector?
See Also
Phase 07: FULL_RESOLUTION
Full resolution is the largest compilation phase, containing 92 errors (43% of all compiler errors). This phase performs complete type resolution, method resolution, operator validation, service definition checking, and control flow analysis. Errors are organized by category for easier navigation.
Control Flow Errors
E07340: Pre Flow Symbol Not Resolved
Classification: PRE_FLOW_SYMBOL_NOT_RESOLVED
Description
The compiler cannot determine the subject of flow control when using complex expressions
involving object access and operators. In expressions like counter.value += 5,
the flow subject is ambiguous - is it counter or value?
This error occurs when a switch or if statement uses such
expressions without explicit clarity about what is being tested.
EK9 requires unambiguous flow subjects. Use either simple identifiers or assign the result to a variable first, then use that variable as the flow subject.
Example (Error)
#!ek9
-
PRE_FLOW_SYMBOL_NOT_RESOLVED: SWITCH with += operator.
objectAccessExpression assignment without explicit control.
-?>
defines module fuzztest.preflow.switch.addassign
defines record
Counter
value <- 0
defines function
switchWithAddAssign()
counter <- Counter()
result <- 0
//SWITCH with objectAccessExpression += (no explicit control)
//Error: Cannot determine flow subject - is it 'counter' or 'value'?
@Error: FULL_RESOLUTION: PRE_FLOW_SYMBOL_NOT_RESOLVED
switch counter.value += 5
case > 3
result := 10
default
result := 20
assert result?
//EOF
Solution
#!ek9
defines module fuzztest.preflow.switch.addassign
defines function
switchWithAddAssignCorrected()
counter <- Counter()
result <- 0
//Solution: Assign to variable first, then switch on it
newValue <- counter.value + 5
counter.value := newValue
switch newValue
case > 3
result := 10
default
result := 20
assert result?
See Also
E07350: Guard Used In Expression
Classification: GUARD_USED_IN_EXPRESSION
Description
Guards (?= or <- with conditional semantics) cannot be used in
expression contexts because they may leave variables uninitialized. When a guard's condition
fails (e.g., the value is unset), the guarded block doesn't execute, meaning any variables
declared within or dependent on the guard remain uninitialized.
EK9's strict initialization checking detects this at **symbol definition time** (Phase 01). Guards are only valid in control flow statement positions (if/switch/while/for headers), never in expression contexts like assignments, returns, or switch expression subjects.
Example (Error)
#!ek9
-
Guards in expressions.
-?>
defines module bad.guards
defines function
currentTemperature() as pure
-> country as String
<- temp as Integer?
if country == "GB"
temp :=? 20
else if country == "DE"
temp :=? 41
else
temp :=? 31
defines program
ASwitchWithGuard()
temperature <- Integer()
//The guard may leave 'resultText' un-initialized if the return from currentTemperature was 'un-set'
//This would cause the switch block not to execute and hence the return of 'result' would not execute
//We'd then have resultText looking like it has been initialised when in fact it has not been.
//While technically you could accept this, on a visual scan it might be hard to work out what is wrong.
//Hence EK9 does not allow guards in expressions like this
@Error: SYMBOL_DEFINITION: GUARD_USED_IN_EXPRESSION
resultText <- switch temperature ?= currentTemperature("GB") with temperature
<- result String: String()
case < 12
result: "Moderate"
assert resultText?
//EOF
Solution
#!ek9
defines module bad.guards
defines program
ASwitchWithGuardCorrected()
temperature <- Integer()
//Solution: Guard in control flow position (if statement)
if temperature ?= currentTemperature("GB")
resultText <- switch temperature
<- result String: String()
case < 12
result: "Moderate"
default
result: "Unknown"
assert resultText?
See Also
- Guard Expressions
- If Statements
- Initialization Checking
E07370: Statement Unreachable
Classification: STATEMENT_UNREACHABLE
Description
The compiler detects that all execution paths within a control flow construct will unconditionally throw an exception, making any implicit continuation unreachable. Even when there are no explicit statements following the exception, the implicit flow continuation (such as a loop's next iteration) becomes illogical. This error is caught early during symbol definition to prevent dead code that cannot execute.
Example (Error)
#!ek9
defines module bad.flowcontrol.examples
defines function
//Failure 1
invalidForLoopOverValues()
//There is an implicit continuation here, even though there are no
//following statements after the exception, this will fail at the first.
//It therefore not logical or normal.
@Error: SYMBOL_DEFINITION: STATEMENT_UNREACHABLE
for item in ["Alpha", "Beta", "Charlie"]
throw Exception("This will always fail")
Solution 1: Add Conditional Logic
#!ek9
defines module bad.flowcontrol.examples
defines function
validForLoopWithCondition()
for item in ["Alpha", "Beta", "Charlie"]
if item == "Beta"
throw Exception("Only fails for Beta")
Stdout().println(item)
Solution 2: Remove Loop if Always Failing
#!ek9
defines module bad.flowcontrol.examples
defines function
simplifiedFailure()
//If the intent is to always throw, simplify
throw Exception("Direct failure without loop")
See Also
E07380: Return Unreachable
Classification: RETURN_UNREACHABLE
Description
The function declares a return value but all execution paths unconditionally throw exceptions, making the return statement unreachable. The compiler detects this during early symbol definition analysis by examining control flow - when both branches of an if/else (or all branches of a switch) throw exceptions, no execution path can reach the return initialization. This represents a contradiction between the function signature (which promises a return) and the implementation (which always throws).
Example (Error)
#!ek9
defines module bad.flowcontrol.examples
defines function
//Failure 2
invalidIfElseStatement3()
@Error: SYMBOL_DEFINITION: RETURN_UNREACHABLE
<- rtn as String: "OK"
condition <- true
if condition
throw Exception("Check If")
else
throw Exception("Check Else")
Solution 1: Remove Return Declaration
#!ek9
defines module bad.flowcontrol.examples
defines function
validThrowFunction()
//No return declaration - function just throws
condition <- true
if condition
throw Exception("Check If")
else
throw Exception("Check Else")
Solution 2: Add Valid Return Path
#!ek9
defines module bad.flowcontrol.examples
defines function
validConditionalFunction()
<- rtn as String: "OK"
condition <- shouldThrow()
if condition
throw Exception("Conditional failure")
//Return is now reachable when condition is false
rtn
See Also
E07390: Pointless Expression
Classification: POINTLESS_EXPRESSION
Description
Using a constant Boolean literal (true/false) as a control flow condition is pointless because the outcome is known at compile time. The compiler detects this during symbol definition to prevent dead code patterns. If a condition is always true, the if-statement serves no purpose - the code should execute unconditionally. If always false, the code block is unreachable and should be removed. This error typically indicates leftover debugging code or a logic mistake where a variable comparison was intended instead of a literal.
Example (Error)
#!ek9
defines module bad.flowcontrol.examples
defines program
//Failure 5
BadProgram1()
@Error: SYMBOL_DEFINITION: POINTLESS_EXPRESSION
if true
x <- 91
assert x?
Solution 1: Remove Pointless Condition
#!ek9
defines module bad.flowcontrol.examples
defines program
GoodProgram1()
//Just execute unconditionally
x <- 91
assert x?
Solution 2: Use Actual Condition
#!ek9
defines module bad.flowcontrol.examples
defines program
GoodProgram2()
condition <- checkSomething()
if condition
x <- 91
assert x?
See Also
Switch Statement Errors
E07310: Not All Enumerated Values Present In Switch
Classification: NOT_ALL_ENUMERATED_VALUES_PRESENT_IN_SWITCH
Description
When using a switch on an enumeration type with explicit value checks (simple case
statements without expressions), EK9 requires exhaustive coverage - all enumerated
values must be present in case statements. This compiler enforcement provides a
critical safety mechanism: when new values are added to an enumeration, all existing
switch statements will fail compilation, forcing developers to explicitly handle the
new cases. If exhaustive checking is not desired, add a default case
or use if/else logic instead.
Example (Error)
#!ek9
defines module bad.switches.enums
defines type
LimitedEnum
A,
B,
C,
D
defines program
CheckForIncompleteEnumerationCasesAsExpression()
val <- LimitedEnum.A
@Error: FULL_RESOLUTION: NOT_ALL_ENUMERATED_VALUES_PRESENT_IN_SWITCH
basicResult <- switch val
<- rtn as String: "OK"
case LimitedEnum.A
rtn: "Just A"
assert basicResult?
Solution 1: Add All Missing Cases
#!ek9
defines module bad.switches.enums
defines program
CheckForCompleteEnumerationCases()
val <- LimitedEnum.A
basicResult <- switch val
<- rtn as String: "OK"
case LimitedEnum.A
rtn: "Just A"
case LimitedEnum.B, LimitedEnum.C, LimitedEnum.D
rtn: "Other values"
assert basicResult?
Solution 2: Add Default Case
#!ek9
defines module bad.switches.enums
defines program
CheckWithDefault()
val <- LimitedEnum.A
basicResult <- switch val
<- rtn as String: "OK"
case LimitedEnum.A
rtn: "Just A"
default
rtn: "All other cases"
assert basicResult?
See Also
E07320: Default Required In Switch Statement
Classification: DEFAULT_REQUIRED_IN_SWITCH_STATEMENT
Description
A default case is required in switch statements to handle all possible
execution paths. Even when switching on an enumeration with all values explicitly
covered, a default is still required because the enumeration variable
itself might be unset. EK9's tri-state object model means variables can be absent,
unset, or set - the default case must handle the unset scenario. Without it, the
compiler cannot guarantee the switch statement handles all execution paths safely.
Example (Error)
#!ek9
defines module bad.switches.enums
defines type
LimitedEnum
A, B, C, D
defines program
MissingDefaultInSwitchStatement1()
result as String?
val <- LimitedEnum.A
//You may think why is a default needed, when all enum values are catered for.
//But don't forget val might not actually be set, so that is also a situation to deal with
@Error: FULL_RESOLUTION: DEFAULT_REQUIRED_IN_SWITCH_STATEMENT
switch val
case LimitedEnum.A
result: "Just A"
case LimitedEnum.B
result: "Just B"
case LimitedEnum.C
result: "Just C"
case LimitedEnum.D
result: "Just D"
assert result?
Solution
#!ek9
defines module bad.switches.enums
defines program
ValidSwitchWithDefault()
result as String?
val <- LimitedEnum.A
switch val
case LimitedEnum.A
result: "Just A"
case LimitedEnum.B
result: "Just B"
case LimitedEnum.C
result: "Just C"
case LimitedEnum.D
result: "Just D"
default
result: "Val is unset"
assert result?
See Also
E07330: Default Required In Switch Expression
Classification: DEFAULT_REQUIRED_IN_SWITCH_EXPRESSION
Description
Switch expressions (switches that return values) require a default case
when the return variable is declared unset (nullable). Even when using expression-based
case conditions (like < LimitedEnum.C), the compiler cannot guarantee
all cases are exhaustively covered without a default. The switch expression must be
able to produce a return value for every possible input, including edge cases not
matched by explicit case conditions. If the return variable were initialized with a
default value, the default case could be omitted.
Example (Error)
#!ek9
defines module bad.switches.enums
defines type
LimitedEnum
A, B, C, D
defines program
InvalidEnumerationWithExpressionUnsetReturn()
val <- LimitedEnum.A
@Error: FULL_RESOLUTION: DEFAULT_REQUIRED_IN_SWITCH_EXPRESSION
basicResult <- switch val
<- rtn as String?
case < LimitedEnum.C
rtn: "Just A or B"
assert basicResult?
Solution
#!ek9
defines module bad.switches.enums
defines program
ValidEnumerationWithDefault()
val <- LimitedEnum.A
basicResult <- switch val
<- rtn as String: "OK"
case < LimitedEnum.C
rtn: "Just A or B"
default
rtn: "Val is not set"
assert basicResult?
See Also
E07360: Application Selection Invalid
Classification: APPLICATION_SELECTION_INVALID
Description
The 'with application of' syntax is exclusively for program/application-level dependency injection and is not valid on methods. This construct enables external configuration to select specific implementations (like database adapters, payment providers, etc.) at deployment time. Attempting to use 'with application of' on a class method makes no sense because method implementation is resolved at compile time through the class definition, not through external application configuration. Use dependency injection via constructors or parameters instead.
Example (Error)
#!ek9
defines module bad.classmodifier.use
defines class
C1 extends C0
@Error: SYMBOL_DEFINITION: APPLICATION_SELECTION_INVALID
methodX with application of abc
v1 <- "Developer some how expecting application injection here"
assert v1?
Solution
#!ek9
defines module example
demo()
value <- 42
result <- switch value
case 1
<- "One"
case 2
<- "Two"
default
<- "Other"
See Also
Returning Block Errors
E07400: Returning Missing
Classification: RETURNING_MISSING
Description
Operators and methods that implement logic (have a body) must declare a return type
using the <- syntax when they produce values. The compiler detects
this during explicit type symbol definition because operators/methods are validated
for completeness at this phase. Even simple operators like ! (factorial
or clear) require an explicit return type declaration - the implementation cannot
infer what type should be returned without explicit guidance from the developer.
Example (Error)
#!ek9
defines module bad.classes.operators.examples2
defines class
C3
//So you can have factorial or clear but it must return some type
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: RETURNING_MISSING
operator ! as pure
val <- 1
assert val?
Solution
#!ek9
defines module bad.classes.operators.examples2
defines class
C3
//Factorial operator must declare return type
operator ! as pure
<- rtn as Integer: 1
rtn
See Also
E07405: Returning Required
Classification: RETURNING_REQUIRED
Description
Switch expressions that assign to a left-hand side variable must declare a returning
block using the <- syntax to specify what value the expression produces.
This is a common mistake for developers new to EK9 - using case assignments
(someResult: "value") without declaring the switch's return type. The
compiler detects during full resolution that the switch expression is trying to return
a value (because it's assigned to a variable) but lacks the required returning declaration.
Example (Error)
#!ek9
defines module bad.switches.use
defines program
InvalidProgramReturnRequired()
val <- "ToCheck"
//Here there is no return value but one is expected
//This is likely to be how someone new to ek9 will get it wrong.
@Error: FULL_RESOLUTION: RETURNING_REQUIRED
someResult <- switch val
case "ToCheck"
someResult: "Jackpot"
case "This"
someResult: "Was this"
case "This"
someResult: "Was that"
default
someResult: "Nothing"
//This will be a Void, but that now does have ? 'isSet' and is always false.
assert someResult?
Solution
#!ek9
defines module bad.switches.use
defines program
ValidProgramWithReturning()
val <- "ToCheck"
//Declare the return type properly
someResult <- switch val
<- resultText as String?
case "ToCheck"
resultText: "Jackpot"
case "This"
resultText: "Was this"
default
resultText: "Nothing"
assert someResult?
See Also
E07406: Returning Not Required
Classification: RETURNING_NOT_REQUIRED
Description
Switch statements (not expressions) used for control flow should not declare returning
blocks because they don't assign to a left-hand side variable - they execute for side
effects only. This is another common mistake for EK9 newcomers: adding a returning
declaration (<- rtn as String) to a switch statement when the switch
is not being assigned to a variable. The compiler detects during full resolution that
the switch has a return declaration but no corresponding assignment target.
Example (Error)
#!ek9
defines module bad.switches.use
defines program
InvalidProgramReturnNotRequired()
val <- "ToCheck"
someResult <- String()
//This is also how someone will get the returning part wrong on a switch.
//In this case there is no lhs variable to return anything into.
switch val
@Error: FULL_RESOLUTION: RETURNING_NOT_REQUIRED
<- rtn as String: String()
case "ToCheck"
someResult: "Jackpot"
case "This"
someResult: "Was this"
case "This"
someResult: "Was that"
default
someResult: "Nothing"
assert someResult?
Solution: Remove Returning Declaration
#!ek9
defines module bad.switches.use
defines program
ValidProgramStatement()
val <- "ToCheck"
someResult <- String()
//Just use switch statement without returning
switch val
case "ToCheck"
someResult: "Jackpot"
case "This"
someResult: "Was this"
default
someResult: "Nothing"
assert someResult?
See Also
E07410: Must Return Same As Construct Type
Classification: MUST_RETURN_SAME_AS_CONSTRUCT_TYPE
Description
Certain operators have strict return type requirements enforced by the EK9 language
specification. The negate operator (~), increment (++), and
decrement (--) operators must return the same type as the class they're
defined in. This constraint exists because these operators represent transformations
of the object itself (negation, increment, decrement) - they fundamentally return a
modified version of the same type. The compiler validates this during explicit type
symbol definition to ensure operator semantics match their mathematical/logical intent.
Example (Error)
#!ek9
defines module bad.classes.operators.examples2
defines class
C2
//The not/negate operator
//As such it accepts no parameters, but returns the same type as itself
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: MUST_RETURN_SAME_AS_CONSTRUCT_TYPE
operator ~ as pure
<- rtn as Float: 1.0
Solution
#!ek9
defines module bad.classes.operators.examples2
defines class
C2
//Negate operator must return C2 type
operator ~ as pure
<- rtn as C2: C2()
See Also
E07420: Must Not Return Same Type
Classification: MUST_NOT_RETURN_SAME_TYPE
Description
The promote operator (#^) has a strict requirement that it must return
a DIFFERENT type than the class it's defined in. Promotion fundamentally means
transforming a value to a higher-level or more general type - returning the same
type defeats the purpose of promotion. This is the opposite constraint from operators
like ~, ++, -- which must return the same type.
The compiler enforces this during explicit type symbol definition to ensure promotion
semantics are maintained.
Example (Error)
#!ek9
defines module bad.classes.operators.examples5
defines class
C5
//This is not a promotion
operator #^ as pure
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: MUST_NOT_RETURN_SAME_TYPE
<- rtn as C5: this
Solution
#!ek9
defines module bad.classes.operators.examples5
defines class
C5
//Promotion must return different type
operator #^ as pure
<- rtn as String: "Promoted to String"
See Also
E07430: Return Value Not Supported
Classification: RETURN_VALUE_NOT_SUPPORTED
Description
Mutation operators like copy (:=:), replace (:^:), and
merge (:~:) fundamentally operate through side effects - they modify
the object in place rather than producing new values. Therefore, return values are
not supported for these operators. The compiler enforces this during explicit type
symbol definition because allowing returns would contradict the mutation semantics
these operators represent. Unlike mathematical operators that transform and return,
mutation operators alter state directly.
Example (Error)
#!ek9
defines module bad.classes.operators.examples3
defines class
//Some of mutation operators. These typically accept something in, but never return anything.
C3
//Copy from arg0 into this, there is no return here
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: RETURN_VALUE_NOT_SUPPORTED
operator :=:
-> arg0 as Float
<- rtn as Float: arg0
assert arg0?
Solution
#!ek9
defines module bad.classes.operators.examples3
defines class
C3
//Copy operator mutates in place, no return
operator :=:
-> arg0 as Float
//Mutate this object with arg0's value
assert arg0?
See Also
E07440: Covariance Mismatch
Classification: COVARIANCE_MISMATCH
Description
When overriding methods in a subclass, the return type must be covariant - either exactly the same type as the base method or a subtype (more specific type). This error occurs when an override attempts to change the return type to an incompatible type that is neither the same nor a subtype. The compiler enforces covariance during full resolution to ensure type safety: code expecting the base class return type must safely handle what the override returns. Note that coercion (like Integer→Float) does NOT satisfy covariance - the type hierarchy relationship must be structural.
Example (Error)
#!ek9
defines module bad.class.covariance
defines class
C1 as open
someMethod1()
-> arg0 as String
<- rtn as Float: 1.0
-
Extend an override the C1 someMethod1 method but alter the return type
so that it incompatible with the class C1
-?>
C2 extends C1
@Error: FULL_RESOLUTION: COVARIANCE_MISMATCH
override someMethod1()
-> arg0 as String
<- rtn as String: "Test"
Solution: Match Base Return Type
#!ek9
defines module bad.class.covariance
defines class
C1 as open
someMethod1()
-> arg0 as String
<- rtn as Float: 1.0
C2 extends C1
override someMethod1()
-> arg0 as String
<- rtn as Float: 2.0 //Same return type as base
See Also
Operator Errors
Operator errors relate to operator definitions, usage restrictions, and purity requirements. EK9 has specific rules about which operators must be pure, operator naming conventions, and limitations on operator usage for certain types.
E07500: Operator Must Be Pure
Classification: OPERATOR_MUST_BE_PURE
Description
Query and comparison operators must be declared as pure in EK9 because
they represent read-only operations that should not produce side effects. The
contains, matches, ==, <>,
<, >, <=, >=,
<=>, ?, and #? operators all fall into
this category. The compiler enforces this during explicit type symbol definition to
ensure these fundamental operations remain side-effect-free, enabling optimizations
and maintaining referential transparency.
Example (Error)
#!ek9
defines module bad.classes.operators.examples5
defines class
C5
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: OPERATOR_MUST_BE_PURE
operator contains
-> arg0 as Float
<- rtn as Boolean: true
Solution
#!ek9
defines module bad.classes.operators.examples5
defines class
C5
operator contains as pure
-> arg0 as Float
<- rtn as Boolean: true
See Also
E07510: Operator Cannot Be Pure
Classification: OPERATOR_CANNOT_BE_PURE
Description
Mutation operators fundamentally modify object state through side effects and therefore
cannot be declared as pure. This includes copy (:=:), replace
(:^:), merge (:~:), increment (++), decrement
(--), and compound assignment operators (+=, -=,
etc.). The compiler enforces this during explicit type symbol definition because allowing
pure mutation operators would violate EK9's purity guarantees - pure functions must not
produce observable side effects. Mutation is the opposite of purity.
Example (Error)
#!ek9
defines module bad.classes.operators.examples3
defines class
//Some of mutation operators. These typically accept something in, but never return anything.
C3
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: OPERATOR_CANNOT_BE_PURE
operator :=: as pure
-> arg0 as C3
assert arg0?
Solution
#!ek9
defines module bad.classes.operators.examples3
defines class
C3
operator :=: //Remove 'as pure' - mutation operators are inherently impure
-> arg0 as C3
assert arg0?
See Also
E07620: Operator Not Defined
Classification: OPERATOR_NOT_DEFINED
Description
An operator required for the expression is not defined on the type being used. This commonly occurs when using
custom types in range expressions (in operator) or collection membership tests, which require comparison
operators (<=>, ==) to be defined. EK9 does not provide default implementations of these
operators on custom types - you must explicitly define them to enable these operations.
Example (Error)
#!ek9
defines module bad.range.collections
defines class
C1
default C1()
//But miss out any operators.
defines function
CheckBadSimpleIntegerRange5()
var <- 16
@Error: FULL_RESOLUTION: OPERATOR_NOT_DEFINED
result <- var in C1() ... C1()
@Error: FULL_RESOLUTION: TYPE_NOT_RESOLVED
assert result
Solution 1: Define Required Operators
#!ek9
defines module bad.range.collections
defines class
C1
value <- 0
default C1()
C1()
-> arg as Integer
value :=: arg
operator <=> as pure
-> arg as C1
<- result as Integer: value <=> arg.value
operator == as pure
-> arg as C1
<- result as Boolean: value == arg.value
defines function
CheckSimpleRange()
var <- 16
result <- var in C1(10) ... C1(30) //Now valid
assert result
Solution 2: Use Built-in Types
#!ek9
defines module bad.range.collections
defines function
CheckSimpleIntegerRange()
var <- 16
result <- var in 10 ... 30 //Built-in Integer has operators
assert result
See Also
E07630: Operator Cannot Be Used On Enumeration
Classification: OPERATOR_CANNOT_BE_USED_ON_ENUMERATION
Description
An operator is being applied to an enumeration type definition itself, not to enumeration values. Operators like
$ (String conversion), $$ (JSON conversion), and comparison operators can only be used on
enumeration instances (values), not on the enumeration type. The enumeration type represents the
complete set of possible values and cannot be converted or compared directly.
Example (Error)
#!ek9
defines module bad.enumeration.use
defines type
CardSuit
Hearts
Diamonds
Clubs
Spades
defines function
InvalidUseOfStringConversion()
@Error: FULL_RESOLUTION: OPERATOR_CANNOT_BE_USED_ON_ENUMERATION
suitAsString <- $CardSuit
@Error: FULL_RESOLUTION: TYPE_NOT_RESOLVED
assert suitAsString?
InvalidUseOfComparisonOperators()
@Error: FULL_RESOLUTION: OPERATOR_CANNOT_BE_USED_ON_ENUMERATION
result <- CardSuit > CardSuit.Hearts
Solution: Use Enumeration Values
#!ek9
defines module bad.enumeration.use
defines type
CardSuit
Hearts
Diamonds
Clubs
Spades
defines function
ValidUseOfEnumeration()
stdout <- Stdout()
cat CardSuit > stdout
for val in CardSuit
stdout.println(`Value is ${val}`)
assert CardSuit.Hearts < CardSuit.Clubs
hearts <- CardSuit.Hearts
assert hearts < CardSuit.Spades
See Also
E07640: Bad Not Equal Operator
Classification: BAD_NOT_EQUAL_OPERATOR
Description
EK9 uses <> for the not-equal operator, not != as in C-family languages. This design
choice provides visual distinctiveness and aligns with mathematical notation. Attempting to define or use !=
as an operator will be rejected during type definition validation. Use <> for inequality comparisons
and implement the <> operator method (not !=) when defining custom types.
Example (Error)
#!ek9
defines module bad.classes.operators.examples1
defines class
C1
//Should use <> for not equals
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: BAD_NOT_EQUAL_OPERATOR
operator != as pure
-> arg0 as C1
<- rtn as Boolean: true
Solution
#!ek9
defines module bad.classes.operators.examples1
defines class
C1
value <- 0
default C1()
C1()
-> arg as Integer
value :=: arg
operator <> as pure
-> arg0 as C1
<- rtn as Boolean: value <> arg0.value
defines function
ValidComparison()
obj1 <- C1(10)
obj2 <- C1(20)
if obj1 <> obj2
stdout.println("Objects are not equal")
See Also
E07650: Bad Not Operator
Classification: BAD_NOT_OPERATOR
Description
EK9 uses ~ (tilde) for the logical NOT operator, not ! or the word not as in
some other languages. This design choice provides consistency with bitwise negation and visual clarity. Attempting to
define an operator not will be rejected during type definition validation. For boolean negation in
expressions and operator method implementations, always use ~.
Example (Error)
#!ek9
defines module bad.classes.operators.examples1
defines class
C1
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: BAD_NOT_OPERATOR
operator not as pure
-> arg0 as C1
<- rtn as Boolean: true
Solution
#!ek9
defines module bad.classes.operators.examples1
defines class
C1
isValid <- true
default C1()
C1()
-> arg as Boolean
isValid :=: arg
operator ~ as pure
<- rtn as Boolean: ~isValid
defines function
ValidNegation()
obj1 <- C1(true)
obj2 <- ~obj1
enabled <- true
if ~enabled
stdout.println("Disabled")
See Also
E07660: Operator Name Used As Method
Classification: OPERATOR_NAME_USED_AS_METHOD
Description
Certain names are reserved exclusively for operator definitions and cannot be used as regular method names. These
reserved names include close, contains, matches, empty, and
length. While these names can be used as parameter or variable names within methods, attempting to
define a method with one of these names will be rejected during symbol definition. This restriction prevents
ambiguity between method calls and operator usage.
Example (Error)
#!ek9
defines module bad.classes.examples
defines class
C3 as abstract
//Try and declare a method with the name of an operator
@Error: SYMBOL_DEFINITION: OPERATOR_NAME_USED_AS_METHOD
close
val <- 1
assert val?
@Error: SYMBOL_DEFINITION: OPERATOR_NAME_USED_AS_METHOD
contains
val <- 1
assert val?
@Error: SYMBOL_DEFINITION: OPERATOR_NAME_USED_AS_METHOD
length
val <- 1
assert val?
Solution: Use Reserved Names as Parameters Only
#!ek9
defines module bad.classes.examples
defines class
C3 as abstract
//Reserved names CAN be used as parameters
closeMethod()
-> close as String
assert close?
containsMethod()
-> contains as String
assert contains?
lengthMethod()
-> length as String
assert length?
//Use different method names
closeConnection()
val <- 1
assert val?
hasValue()
val <- 1
assert val?
getLength()
val <- 1
assert val?
See Also
E07670: Service Operator Not Supported
Classification: SERVICE_OPERATOR_NOT_SUPPORTED
Description
Services cannot define custom operators as HTTP endpoint mappings. Service definitions support HTTP method mappings
(GET, POST, PUT, DELETE, etc.) with URI paths, but attempting to define an operator (like <~>)
as a service endpoint will be rejected. Services are designed for HTTP request/response handling, and operator
semantics do not apply to web service endpoints. Use standard HTTP methods for service operations.
Example (Error)
#!ek9
defines module bad.services.use
defines service
Addresses :/addresses
//An invalid operator
@Error: SYMBOL_DEFINITION: SERVICE_OPERATOR_NOT_SUPPORTED
operator <~> :/
-> request as HTTPRequest :=: REQUEST
<- response as HTTPResponse: (request) trait HTTPResponse by delegate as class
override content()
<- rtn as String: getNotOK()
Solution: Use HTTP Method Mappings
#!ek9
defines module bad.services.use
defines service
Addresses :/addresses
byId() as GET for :/{address-id}
-> addressId as AddressId :=: PATH "address-id"
<- response as HTTPResponse: (addressId) trait HTTPResponse by delegate as class
override content()
<- rtn as String: getOK()
listAll() as GET for :/
<- response as HTTPResponse: () trait HTTPResponse by delegate as class
override content()
<- rtn as String: getAllAddresses()
create() as POST for :/
-> request as HTTPRequest :=: REQUEST
<- response as HTTPResponse: (request) trait HTTPResponse by delegate as class
override content()
<- rtn as String: createAddress()
See Also
Dispatcher, Iteration, Exception, and Mutability Errors
These errors relate to EK9's dispatcher mechanism for dynamic method resolution, iteration support via pipe operators, exception handling restrictions, and mutability constraints.
E07810: Dispatch Only Supported In Classes
Classification: DISPATCH_ONLY_SUPPORTED_IN_CLASSES
Description
Dispatcher methods (marked with as dispatcher) are only supported in classes, not in traits, components,
or other constructs. Dispatchers provide dynamic method resolution based on parameter types, which requires the
concrete implementation context that only classes provide. Traits define interfaces and cannot have dispatcher
entry points. Use classes when you need dispatcher functionality.
Example (Error)
#!ek9
defines module bad.traits.examples
defines trait
T1
@Error: SYMBOL_DEFINITION: DISPATCH_ONLY_SUPPORTED_IN_CLASSES
anotherMethod() as dispatcher
-> arg0 as String
assert arg0?
Solution: Use Class Instead
#!ek9
defines module bad.traits.examples
defines class
C1
anotherMethod() as dispatcher
-> arg0 as String
assert arg0?
anotherMethod()
-> arg0 as Integer
assert arg0?
anotherMethod()
-> arg0 as Float
assert arg0?
See Also
E07820: Dispatchers Only Have One Method Entry Point Marked
Classification: DISPATCHERS_ONLY_HAVE_ONE_METHOD_ENTRY_POINT_MARKED
Description
Only one method in a class can be marked as a dispatcher entry point (using as dispatcher). When you
have multiple overloaded methods with the same name, exactly ONE must be designated as the dispatcher entry point
that receives all calls and dynamically dispatches to the appropriate specialized method based on parameter types.
All other overloaded methods should NOT be marked as dispatcher—they are the handler methods that the
dispatcher delegates to.
Example (Error)
#!ek9
defines module bad.dispatchermethods
defines class
BadDispatcher6
@Error: FULL_RESOLUTION: DISPATCHERS_ONLY_HAVE_ONE_METHOD_ENTRY_POINT_MARKED
process() as dispatcher
@Error: FULL_RESOLUTION: INCOMPATIBLE_PARAMETER_GENUS
-> arg as Any
assert arg?
@Error: FULL_RESOLUTION: DISPATCHERS_ONLY_HAVE_ONE_METHOD_ENTRY_POINT_MARKED
process() as dispatcher
-> arg as Integer
assert arg?
@Error: FULL_RESOLUTION: DISPATCHERS_ONLY_HAVE_ONE_METHOD_ENTRY_POINT_MARKED
process() as dispatcher
-> arg as Float
assert arg?
Solution
#!ek9
defines module bad.dispatchermethods
defines class
ValidDispatcher1 as open
private process() as dispatcher //Single dispatcher entry point
-> arg as Any
assert arg?
//Regular handler methods (not marked as dispatcher)
protected process()
-> arg as Float
assert arg?
process()
-> arg as Integer
assert arg?
See Also
E07830: Unable To Find Pipe For Type
Classification: UNABLE_TO_FIND_PIPE_FOR_TYPE
Description
The stream terminator (the target receiving piped data using >) does not have an operator |
that accepts the type being piped to it. For a type to receive piped data, it must define an operator |
method that accepts the incoming type. This error occurs when you try to pipe data of one type into a collector or
sink that expects a different, incompatible type. Check that the target type's operator | accepts the
type being streamed.
Example (Error)
#!ek9
defines module bad.streams2
defines class
DurationCollector
total as Duration: Duration()
operator |
-> arg0 as Duration //Only accepts Duration
assert arg0?
defines function
BrokenStatementTerminator4()
collector <- DurationCollector()
@Error: FULL_RESOLUTION: UNABLE_TO_FIND_PIPE_FOR_TYPE
cat [1, 2, 3] > collector //Trying to pipe Integer into Duration collector
Solution 1: Use Compatible Types
#!ek9
defines module bad.streams2
defines class
DurationCollector
total as Duration: Duration()
operator |
-> arg0 as Duration
assert arg0?
defines function
SimpleStreamCat2()
collector <- DurationCollector()
cat [P2D, P1W, PT2H] > collector //Pipe Duration into Duration collector
assert collector?
Solution 2: Implement Operator For Required Type
#!ek9
defines module bad.streams2
defines class
DurationCollector
total as Duration: Duration()
operator |
-> arg0 as Duration
assert arg0?
operator |
-> arg0 as Integer //Add support for Integer
total += Duration("PT" + $arg0 + "S")
See Also
E07840: Missing Iterate Method
Classification: MISSING_ITERATE_METHOD
Description
The type being iterated does not provide the required methods for iteration. EK9 supports iteration via two patterns:
(1) an iterator() method returning an Iterator<T>, or (2) duck-typed
hasNext() returning Boolean and next() returning the element type. Primitive
types like Integer, Float, and Boolean are not iterable. Custom classes must implement one of these patterns
completely—partial implementations (missing methods or incorrect return types) are rejected.
Example (Error)
#!ek9
defines module bad.forloops.check
defines class
C1
hasNext()
<- rtn <- false
//Missing next() method
defines function
InvalidForLoop1()
@Error: FULL_RESOLUTION: MISSING_ITERATE_METHOD
for value in 34 //Integer is not iterable
@Error: FULL_RESOLUTION: TYPE_NOT_RESOLVED
assert value?
InvalidForLoop2()
@Error: FULL_RESOLUTION: MISSING_ITERATE_METHOD
for value in C1() //C1 has hasNext() but missing next()
@Error: FULL_RESOLUTION: TYPE_NOT_RESOLVED
assert value?
Solution: Implement Complete Iterator Protocol
#!ek9
defines module bad.forloops.check
defines class
C3
hasNext()
<- rtn <- false
next()
<- rtn as String: String()
defines function
ForLoopStatement3()
for value in C3() //Complete duck-typed iterator
assert value?
See Also
E07850: Single Exception Only
Classification: SINGLE_EXCEPTION_ONLY
Description
The catch block can only accept a single exception parameter, not multiple exception types. EK9's exception handling
model is simplified compared to languages like Java that support multiple catch clauses. Each try-catch block handles
exactly one exception type. If you need to catch multiple exception types, use nested try-catch blocks or catch a
common base exception type (like Exception) and handle different cases within the catch block.
Example (Error)
#!ek9
defines module bad.trycatchfinally.example
defines function
TryCatchMultipleExceptions()
someValue <- 0
try
value <- exceptionFunction()
assert value?
someValue: value
catch
@Error: FULL_RESOLUTION: SINGLE_EXCEPTION_ONLY
->
ex1 as BespokeException
ex2 as Exception
assert ex1? and ex2?
someValue: -1
assert someValue?
Solution 1: Catch Single Base Exception
#!ek9
defines module bad.trycatchfinally.example
defines function
TryCatchSingleException()
someValue <- 0
try
value <- exceptionFunction()
assert value?
someValue: value
catch
-> ex as Exception //Single exception parameter
assert ex?
someValue: -1
assert someValue?
Solution 2: Nested Try-Catch for Different Types
#!ek9
defines module bad.trycatchfinally.example
defines function
NestedTryCatch()
someValue <- 0
try
value <- bespokeExceptionFunction()
assert value?
someValue: value
catch
-> ex as BespokeException
someValue: -1
try
value <- exceptionFunction()
assert value?
someValue: value
catch
-> ex as Exception
someValue: -2
See Also
E07890: Not Mutable
Classification: NOT_MUTABLE
Description
An attempt is being made to mutate an immutable value. Constants (defined with defines constant) and
enumeration values are inherently immutable and cannot be modified, assigned to, or used with mutation operators like
++, --, +=, or assignment operators like :=, :=:,
:^:, :~:. Additionally, trying to mutate the result of a method call that returns nothing
(void) will trigger this error. Also while loop variables can and do change, they are not mutable directly.
So for example you cannot modify and mutate 'i' in a 'for i' style loop.
Example (Error)
#!ek9
defines module bad.mutations
defines constant
NAME <- "STEVE"
NUMBERS <- 1
DATE <- 2023-11-03
defines function
CheckNoMutationOfConstant()
@Error: FULL_RESOLUTION: NOT_MUTABLE
NUMBERS++
@Error: FULL_RESOLUTION: NOT_MUTABLE
DATE += P2D
CheckNoAssignmentToConstant()
@Error: FULL_RESOLUTION: NOT_MUTABLE
NAME: "Stephen"
@Error: FULL_RESOLUTION: NOT_MUTABLE
NAME += "Snake"
Solution: Use Variables Instead of Constants
#!ek9
defines module bad.mutations
defines constant
INITIAL_NAME <- "STEVE"
INITIAL_NUMBER <- 1
INITIAL_DATE <- 2023-11-03
defines function
CheckMutationOfVariableIsOK()
//mutation to a variable is fine.
var <- 1
var++
var--
var += 6
assert var?
date <- 2023-11-03
date += P2D
assert date?
See Also
- Variables
- Mutability
E07900: Invalid Value
Classification: INVALID_VALUE
Description
A value provided does not meet the required format or constraints for its context. This commonly occurs with text
construct language codes, which must follow the pattern [a-z]+(_[A-Z]+)? (lowercase language code,
optional underscore followed by uppercase country code). Examples of valid codes: "en", "de",
"en_GB", "fr_FR". Invalid formats include uppercase base codes, hyphens instead of
underscores, or incorrect case for country codes.
Example (Error)
#!ek9
defines module bad.text.language.codes
//Invalid: uppercase base (should be lowercase)
@Error: SYMBOL_DEFINITION: INVALID_VALUE
defines text for "EN"
TestText1
welcome()
"Welcome"
//Invalid: hyphen instead of underscore
@Error: SYMBOL_DEFINITION: INVALID_VALUE
defines text for "en-US"
TestText3
message()
"Hi there"
//Invalid: lowercase country code
@Error: SYMBOL_DEFINITION: INVALID_VALUE
defines text for "en_us"
TestText4
welcome()
"Welcome"
Solution: Use Correct Language Code Format
#!ek9
defines module bad.text.language.codes
defines text for "en"
TestText1
welcome()
"Welcome"
defines text for "en_US"
TestText3
message()
"Hi there"
defines text for "en_GB"
TestText4
welcome()
"Welcome"
See Also
Web Service Errors
Web service errors relate to EK9's HTTP service definitions, including URI patterns, HTTP verbs, parameter handling, and return types. EK9 services provide built-in HTTP capabilities with strong type checking.
E07680: Service URI With Vars Not Supported
Classification: SERVICE_URI_WITH_VARS_NOT_SUPPORTED
Description
The service base URI (the root path defined in the service declaration) cannot contain path variables. While
individual HTTP method endpoints can use path variables (like :/{id}), the service's root URI must
be a fixed path. This ensures that the service has a stable base address for routing, with variable paths handled
at the HTTP method level. Move path variables from the service base URI to the specific HTTP method mappings.
Example (Error)
#!ek9
defines module bad.services.use
defines service
//The Name of the and the uri it is mapped to - but break the code by using a parameter.
@Error: SYMBOL_DEFINITION: SERVICE_URI_WITH_VARS_NOT_SUPPORTED
Addresses :/addresses/{not-allowed}
byId() as GET for :/{address-id}
-> addressId as AddressId :=: PATH "address-id"
<- response as HTTPResponse: (addressId) trait HTTPResponse by delegate as class
override content()
<- rtn as String: getOK()
Solution: Use Fixed Service URI, Variables in Methods
#!ek9
defines module bad.services.use
defines service
Addresses :/addresses //Fixed base URI
byId() as GET for :/{address-id} //Variables allowed here
-> addressId as AddressId :=: PATH "address-id"
<- response as HTTPResponse: (addressId) trait HTTPResponse by delegate as class
override content()
<- rtn as String: getOK()
listAll() as GET for :/ //No variables needed
<- response as HTTPResponse: () trait HTTPResponse by delegate as class
override content()
<- rtn as String: getAllAddresses()
See Also
E07690: Service HTTP Access Not Supported
Classification: SERVICE_HTTP_ACCESS_NOT_SUPPORTED
Description
HTTP access annotations (:=: REQUEST, :=: CONTENT, :=: PATH, etc.) can only
be used in HTTP-mapped methods (GET, POST, PUT, DELETE, etc.), not in regular service methods (private or public
helper methods). These annotations provide direct access to HTTP request data and are meaningful only in the context
of handling HTTP requests. Helper methods that don't map to HTTP endpoints cannot use these annotations because
there is no HTTP request/response to access.
Example (Error)
#!ek9
defines module bad.services.use
defines service
Addresses :/addresses
//Not allowed because this is just a normal method and 'correlation' to http is not possible.
@Error: SYMBOL_DEFINITION: IMPLEMENTATION_MUST_BE_PROVIDED
private someMethod()
@Error: SYMBOL_DEFINITION: SERVICE_HTTP_ACCESS_NOT_SUPPORTED
-> incomingContent as String :=: CONTENT
@Error: SYMBOL_DEFINITION: IMPLEMENTATION_MUST_BE_PROVIDED
private anotherInvalidMethod()
@Error: SYMBOL_DEFINITION: SERVICE_HTTP_ACCESS_NOT_SUPPORTED
<- incomingContent as String :=: CONTENT
Solution: Use HTTP Annotations Only in HTTP Methods
#!ek9
defines module bad.services.use
defines service
Addresses :/addresses
//HTTP annotations allowed in HTTP-mapped methods
create() as POST for :/
-> incomingContent as String :=: CONTENT
<- response as HTTPResponse: processContent(incomingContent)
byId() as GET for :/{address-id}
-> addressId as AddressId :=: PATH "address-id"
<- response as HTTPResponse: getAddress(addressId)
//Helper methods use normal parameters (no HTTP annotations)
private processContent()
-> content as String
<- response as HTTPResponse: createResponse(content)
private getAddress()
-> id as AddressId
<- response as HTTPResponse: lookupAddress(id)
See Also
E07700: Service HTTP Path Param Invalid
Classification: SERVICE_HTTP_PATH_PARAM_INVALID
Description
The HTTP path parameter name does not match any variable declared in the URI pattern. When using :=: PATH
"parameter-name", the string literal must exactly match a path variable in the URI (defined with
:/{variable-name}). When using implicit PATH (just the parameter name without explicit qualifier),
the parameter name must match a URI path variable. Check for typos, case mismatches, or incorrect parameter names.
Example (Error)
#!ek9
defines module bad.servicemethod.argumenttypes
defines service
S2 for :/site1/place2
invalid5() :/{incorrect-name}/invalid5.html
->
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: SERVICE_HTTP_PATH_PARAM_INVALID
arg0 as Integer :=: PATH "no-such-path" //Name doesn't match URI variable
<-
response as HTTPResponse: () with trait of HTTPResponse
invalid6() :/{non-such}/invalid6.html
->
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: SERVICE_HTTP_PATH_PARAM_INVALID
nonSuch as Integer //Assume PATH but 'nonSuch' != 'non-such'
<-
response as HTTPResponse: () with trait of HTTPResponse
Solution: Match URI Path Variable Names
#!ek9
defines module bad.servicemethod.argumenttypes
defines service
S2 for :/site1/place2
index4() :/{address-id}
-> addressId as Integer :=: PATH "address-id" //Explicit match
<- response as HTTPResponse: () with trait of HTTPResponse
index5() :/{address-id}/{routeId}/someResource.xml
->
addressId as Integer :=: PATH "address-id"
routeId as String //Implicit PATH, name matches URI variable
<-
response as HTTPResponse: () with trait of HTTPResponse
See Also
E07710: Service HTTP Param Needs Qualifier
Classification: SERVICE_HTTP_PARAM_NEEDS_QUALIFIER
Description
HTTP parameters accessed via HEADER, QUERY, or PATH require a string qualifier specifying the parameter name from
the HTTP request. The syntax :=: HEADER, :=: QUERY, or :=: PATH without a
following string literal is incomplete. You must provide the actual HTTP parameter name as a string, like
:=: HEADER "Authorization" or :=: QUERY "page". Only REQUEST does not require a qualifier
since it represents the entire request object.
Example (Error)
#!ek9
defines module bad.servicemethod.argumenttypes
defines service
S2 for :/site1/place2
invalid8() :/invalid8.html
->
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: SERVICE_HTTP_PARAM_NEEDS_QUALIFIER
arg0 as Integer :=: HEADER //Missing parameter name qualifier
<-
response as HTTPResponse: () with trait of HTTPResponse
Solution: Add String Qualifier
#!ek9
defines module bad.servicemethod.argumenttypes
defines service
S2 for :/site1/place2
index2() :/{index-number}/index2.html
->
queryId as Integer :=: PATH "index-number"
fromDate as Date :=: QUERY "from"
duration as Duration :=: QUERY "duration"
timeOut as Millisecond :=: QUERY "time-out"
<-
response as HTTPResponse: () with trait of HTTPResponse
See Also
E07720: Service HTTP Param Qualifier Not Allowed
Classification: SERVICE_HTTP_PARAM_QUALIFIER_NOT_ALLOWED
Description
The REQUEST parameter type does not accept a string qualifier. While QUERY, HEADER, and PATH require string qualifiers
to specify which HTTP parameter to access (e.g., :=: QUERY "page"), REQUEST represents the entire HTTP
request object and should be used as :=: REQUEST without any additional string. Providing a string
qualifier after REQUEST is invalid syntax.
Example (Error)
#!ek9
defines module bad.servicemethod.argumenttypes
defines service
S2 for :/site1/place2
invalid9() :/invalid9.html
->
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: SERVICE_HTTP_PARAM_QUALIFIER_NOT_ALLOWED
arg0 as HTTPRequest :=: REQUEST "not-required" //String qualifier not allowed
<-
response as HTTPResponse: () with trait of HTTPResponse
Solution: Remove String Qualifier
#!ek9
defines module bad.servicemethod.argumenttypes
defines service
S2 for :/site1/place2
index3() :/index3.html
->
request as HTTPRequest :=: REQUEST //No qualifier needed
<-
response as HTTPResponse: () with trait of HTTPResponse
//String qualifiers only for QUERY, HEADER, PATH
index2() :/{index-number}/index2.html
->
queryId as Integer :=: PATH "index-number" //Qualifier required
fromDate as Date :=: QUERY "from" //Qualifier required
<-
response as HTTPResponse: () with trait of HTTPResponse
See Also
E07730: Service HTTP Path Param Count Invalid
Classification: SERVICE_HTTP_PATH_PARAM_COUNT_INVALID
Description
The number of path variables in the URI pattern does not match the number of PATH parameters declared in the method
signature. Every path variable defined in the URI (using :/{variable-name}) must have a corresponding
parameter declaration. If the URI contains two path variables, the method must declare exactly two PATH parameters.
This ensures all URI components can be properly mapped to method parameters.
Example (Error)
#!ek9
defines module bad.servicemethod.argumenttypes
defines service
S2 for :/site1/place2
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: SERVICE_HTTP_PATH_PARAM_COUNT_INVALID
invalid7() :/{validationNumber}/{undefined}/invalid7.html
-> validationNumber as Integer //Missing parameter for {undefined}
<- response as HTTPResponse: () with trait of HTTPResponse
Solution: Declare All Path Parameters
#!ek9
defines module bad.servicemethod.argumenttypes
defines service
S2 for :/site1/place2
index5() :/{address-id}/{routeId}/someResource.xml
->
addressId as Integer :=: PATH "address-id"
routeId as String //Both path variables declared
<-
response as HTTPResponse: () with trait of HTTPResponse
//Single variable - one parameter
index4() :/{address-id}
-> addressId as Integer :=: PATH "address-id"
<- response as HTTPResponse: () with trait of HTTPResponse
See Also
E07740: Service With No Body Provided
Classification: SERVICE_WITH_NO_BODY_PROVIDED
Description
Service methods must have a complete implementation body. EK9 services do not support abstract or unimplemented methods. Every service operation (GET, POST, PUT, DELETE, etc.) must provide the implementation logic that returns an HTTPResponse. This is enforced during the explicit type symbol definition phase to ensure all web service endpoints are fully implemented before the service can be compiled and deployed.
Example (Error)
#!ek9
defines module bad.servicemethod.returntypes
defines service
S1 for :/site1/place1
//Service method without implementation - only declaration
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: SERVICE_WITH_NO_BODY_PROVIDED
anotherMethod() as GET for :/yetAnother/path
<- response as HTTPResponse?
Solution: Provide Implementation
#!ek9
defines service
MyService
GET for "/api/data"
<- result as HTTPResponse: HTTPResponse().ok("{\"status\": \"success\"}")
POST for "/api/data"
-> request as HTTPRequest
<- result as HTTPResponse
//Process request
data <- request.body()
result := HTTPResponse().created("{\"id\": 123}")
See Also
E07750: Service Incompatible Return Type
Classification: SERVICE_INCOMPATIBLE_RETURN_TYPE
Description
EK9 web service operations must return HTTPResponse. Service methods cannot return primitive types (Integer, String, Boolean, etc.) or domain objects directly. The HTTPResponse type is specifically designed to handle HTTP status codes, headers, and content formatting. This requirement ensures that all HTTP responses are properly structured with status codes, content types, and other HTTP-specific metadata. The compiler enforces this during explicit type symbol definition to catch return type mismatches early in compilation.
Example (Error)
#!ek9
defines module fuzztest.service.return.integer
defines service
IntegerReturnService :/numbers
- Returning Integer instead of HTTPResponse -?>
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: SERVICE_INCOMPATIBLE_RETURN_TYPE
getCount() as GET :/count
<- count as Integer: 42
Solution: Return HTTPResponse
#!ek9
defines service
MyService
GET for "/api/data"
<- result as HTTPResponse: HTTPResponse().ok("Hello")
GET for "/api/json"
<- result as HTTPResponse
data <- "{\"message\": \"Hello\"}"
result := HTTPResponse().ok(data).contentType("application/json")
See Also
E07760: Service Incompatible Param Type
Classification: SERVICE_INCOMPATIBLE_PARAM_TYPE
Description
HTTP service parameters (QUERY, PATH, HEADER, CONTENT) support only a limited set of built-in types that can be
reliably parsed from HTTP request data: Integer, String, Date,
Time, DateTime, Millisecond, and Duration. Types like
Float, Money, and custom classes are not supported because HTTP parameters are text-based
and EK9 requires unambiguous, safe conversion. Use supported types or parse complex data from the request body.
Example (Error)
#!ek9
defines module bad.servicemethod.argumenttypes
defines service
S2 for :/site1/place2
invalid1() :/invalid1.html
->
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: SERVICE_INCOMPATIBLE_PARAM_TYPE
arg0 as Float :=: QUERY "some-param" //Float not supported
<-
response as HTTPResponse: () with trait of HTTPResponse
Solution: Use Supported Types
#!ek9
defines module bad.servicemethod.argumenttypes
defines service
S2 for :/site1/place2
index2() :/{index-number}/index2.html
->
queryId as Integer :=: PATH "index-number"
fromDate as Date :=: QUERY "from"
duration as Duration :=: QUERY "duration"
timeOut as Millisecond :=: QUERY "time-out"
coverEnd as DateTime :=: QUERY "end-date-time"
graceTime as Time :=: QUERY "grace-time"
content as String :=: CONTENT
<-
response as HTTPResponse: () with trait of HTTPResponse
See Also
E07770: Service Incompatible Param Type Request
Classification: SERVICE_INCOMPATIBLE_PARAM_TYPE_REQUEST
Description
When using :=: REQUEST to access the full HTTP request, the parameter type must be
HTTPRequest, not any other type like String, Integer, or custom types. The REQUEST qualifier specifically
binds to the complete request object, so only HTTPRequest is compatible. If you need specific data from the request
(like body content or headers), either use HTTPRequest and extract what you need, or use specific qualifiers like
:=: CONTENT or :=: HEADER with appropriate types.
Example (Error)
#!ek9
defines module bad.servicemethod.argumenttypes
defines service
S2 for :/site1/place2
invalid2() :/invalid2.html
->
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: SERVICE_INCOMPATIBLE_PARAM_TYPE_REQUEST
arg0 as String :=: REQUEST //Must be HTTPRequest, not String
<-
response as HTTPResponse: () with trait of HTTPResponse
Solution: Use HTTPRequest Type
#!ek9
defines module bad.servicemethod.argumenttypes
defines service
S2 for :/site1/place2
index3() :/index3.html
->
request as HTTPRequest :=: REQUEST //Correct type
<-
response as HTTPResponse: () with trait of HTTPResponse
See Also
E07780: Service Request By Itself
Classification: SERVICE_REQUEST_BY_ITSELF
Description
When using HTTPRequest as a parameter (via :=: REQUEST), it must be the ONLY parameter in the method
signature. You cannot combine HTTPRequest with other HTTP parameters like QUERY, PATH, HEADER, or CONTENT. This is
because HTTPRequest already provides access to all request data (path parameters, query strings, headers, body), so
mixing it with other parameter bindings would be redundant and ambiguous. Choose either HTTPRequest alone or
individual parameter bindings.
Example (Error)
#!ek9
defines module bad.servicemethod.argumenttypes
defines service
S2 for :/site1/place2
invalid4() :/invalid4.html
->
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: SERVICE_REQUEST_BY_ITSELF
arg0 as HTTPRequest :=: REQUEST
arg1 as Integer :=: QUERY "some-query" //Cannot mix REQUEST with others
<-
response as HTTPResponse: () with trait of HTTPResponse
Solution 1: Use HTTPRequest Alone
#!ek9
defines module bad.servicemethod.argumenttypes
defines service
S2 for :/site1/place2
index3() :/index3.html
->
request as HTTPRequest :=: REQUEST //Only parameter
<-
response as HTTPResponse: () with trait of HTTPResponse
Solution 2: Use Individual Parameters
#!ek9
defines module bad.servicemethod.argumenttypes
defines service
S2 for :/site1/place2
index2() :/{index-number}/index2.html
->
queryId as Integer :=: PATH "index-number"
fromDate as Date :=: QUERY "from"
content as String :=: CONTENT
<-
response as HTTPResponse: () with trait of HTTPResponse
See Also
E07790: Service Incompatible Param Type Non Request
Classification: SERVICE_INCOMPATIBLE_PARAM_TYPE_NON_REQUEST
Description
The HTTPRequest type cannot be used with QUERY, PATH, HEADER, or CONTENT qualifiers. HTTPRequest is specifically
designed to work with the :=: REQUEST qualifier to access the entire request object. If you want to
access specific parts of the request (query parameters, path variables, headers, or body content), use the appropriate
built-in types (String, Integer, Date, etc.) with the corresponding qualifiers, not HTTPRequest. This error occurs when
attempting to declare a parameter like arg as HTTPRequest :=: QUERY "param".
Example (Error)
#!ek9
defines module bad.servicemethod.argumenttypes
defines service
S2 for :/site1/place2
invalid3() :/invalid3.html
->
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: SERVICE_INCOMPATIBLE_PARAM_TYPE_NON_REQUEST
arg0 as HTTPRequest :=: QUERY "another-parameter" //HTTPRequest not compatible with QUERY
<-
response as HTTPResponse: () with trait of HTTPResponse
Solution: Use Appropriate Types for Each Qualifier
#!ek9
defines module bad.servicemethod.argumenttypes
defines service
S2 for :/site1/place2
//Use HTTPRequest only with REQUEST
index3() :/index3.html
->
request as HTTPRequest :=: REQUEST
<-
response as HTTPResponse: () with trait of HTTPResponse
//Use appropriate types for QUERY, PATH, etc.
index2() :/{index-number}/index2.html
->
queryId as Integer :=: PATH "index-number"
fromDate as Date :=: QUERY "from"
content as String :=: CONTENT
<-
response as HTTPResponse: () with trait of HTTPResponse
See Also
E07800: Service Missing Return
Classification: SERVICE_MISSING_RETURN
Description
Every EK9 web service method must explicitly declare a return value of type HTTPResponse. Service operations cannot be void - they must always produce an HTTP response to send back to the client. This is a fundamental requirement of web service implementations: every HTTP request must receive an HTTP response with appropriate status code, headers, and content. The compiler enforces this during explicit type symbol definition, ensuring that no service endpoint can be compiled without a declared HTTPResponse return value.
Example (Error)
#!ek9
defines module fuzztest.service.return.missing.method
defines service
MissingReturnMethodService :/resources
- GET method without return -?>
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: SERVICE_MISSING_RETURN
getResource() as GET :/{id}
-> id as String
stdout <- Stdout()
stdout.println(id)
Solution: Declare HTTPResponse Return
#!ek9
defines service
MyService
GET for "/api/data"
<- result as HTTPResponse //Declare return
result := HTTPResponse().ok("{\"status\": \"ok\"}")
POST for "/api/create"
-> request as HTTPRequest
<- result as HTTPResponse
data <- request.body()
result := HTTPResponse().created("{\"id\": 123}")
See Also
Function and Delegate Errors
Function and delegate errors relate to function parameter requirements, return type constraints, function delegates, and program argument handling. EK9's functional programming features require specific signatures for different use cases.
E07450: Function Must Have No Parameters
Classification: FUNCTION_MUST_HAVE_NO_PARAMETERS
Description
The function must have no parameters for this context. Stream operations like 'head', 'tail', and 'skip' can accept functions to dynamically determine how many elements to take/skip, but these functions must not accept any parameters - they simply return an Integer indicating the count. The function is called at stream execution time to get the dynamic value. If your function requires parameters, you cannot use it in this context - use a fixed integer value or a parameterless function instead.
Example (Error)
#!ek9
defines module bad.streams10
defines function
AcceptsArgument()
-> arg0 as Integer
<- rtn as Integer: arg0
InvalidStreamCatHeadWithFunction2()
collection <- List() of String
@Error: FULL_RESOLUTION: FUNCTION_MUST_HAVE_NO_PARAMETERS
cat ["last", "first"] | head AcceptsArgument > collection
assert collection?
Solution
#!ek9
defines function
generate() //No parameters
<- result as String: "Generated"
//Use in supplier context
supplier <- generate //Function reference
See Also
E07460: Function Must Have Single Parameter
Classification: FUNCTION_MUST_HAVE_SINGLE_PARAMETER
Description
The function must have exactly one parameter for this stream operation context. Operations like 'uniq', 'map', 'filter', and 'select' require functions that accept a single parameter (the current stream element) and perform transformation, filtering, or uniqueness checking. The function signature must match: one input parameter and appropriate return type for the operation. Functions with zero parameters, two parameters, or more cannot be used in these single-parameter contexts.
Example (Error)
#!ek9
defines module bad.streams14
defines function
JustACouple()
<- rtn <- 3
StreamCatExpressionTailWithFunctionInvalidUniq()
collection <- cat [4, 3, 2, 9, 3, 4, 1, 5, 5, 7]
| filter with GreaterThanCheck
@Error: FULL_RESOLUTION: FUNCTION_MUST_HAVE_SINGLE_PARAMETER
| uniq JustACouple
| sort
| tail by JustACouple
| collect as List of Integer
assert collection?
Solution
#!ek9
defines function
transform()
-> value as Integer //Single parameter
<- result as String: String(value * 2)
defines function
demo()
numbers <- [1, 2, 3]
result <- cat numbers | map with transform | collect as List of String
See Also
E07470: Function Must Have Two Parameters
Classification: FUNCTION_MUST_HAVE_TWO_PARAMETERS
Description
The function must have exactly two parameters for this stream operation context. Comparator functions used with 'sort' require two parameters of the same type to compare elements. Similarly, 'join' operations require functions accepting two parameters (one from each stream) to determine how to combine elements. The comparator signature must be: two input parameters of type T, returning Integer (-1, 0, or 1 for less-than, equal, greater-than). Functions with fewer or more than two parameters cannot satisfy this contract.
Example (Error)
#!ek9
defines module bad.streams6
defines function
InvalidProp1ComparatorD()
->
o2 as R1
<-
rtn as Integer: 1
InvalidComparatorFunctionStreamCatSort4()
collector <- StringCollector()
@Error: FULL_RESOLUTION: FUNCTION_MUST_HAVE_TWO_PARAMETERS
cat [R1("last", 2010-10-01), R1("last", 2010-10-02), R1("first", 2010-10-01)] | sort with InvalidProp1ComparatorD > collector
assert collector?
Solution
#!ek9
defines function
combine()
->
accumulator as Integer
value as Integer //Two parameters
defines function
demo()
numbers <- [1, 2, 3, 4, 5]
sum <- cat numbers | reduce by combine
See Also
E07480: Not A Function Delegate
Classification: NOT_A_FUNCTION_DELEGATE
Description
The variable being called with function syntax is not a function delegate. Function delegates are references to executable code (functions, abstract functions) that can be invoked. Regular variables like integers, strings, or objects cannot be called with function invocation syntax `()` unless they are actual function delegates. This error occurs when attempting to invoke a non-callable value as if it were a function. Ensure the variable holds a function reference or use the correct access syntax for the type.
Example (Error)
#!ek9
defines module bad.functiondelegates.examples
defines function
SomeFunction()
<- rtn <- true
AnotherFunction()
nonDelegate <- 1
assert nonDelegate?
//Check to make sure this can be detected, it's just an integer
@Error: FULL_RESOLUTION: NOT_A_FUNCTION_DELEGATE
notValidResult <- nonDelegate()
@Error: FULL_RESOLUTION: TYPE_NOT_RESOLVED
assert notValidResult?
Solution: Use Function Reference
#!ek9
defines function
transformer()
-> value as Integer
<- result as String: String(value * 2)
defines function
demo()
result <- cat [1, 2, 3] | map with transformer | collect as List of String
See Also
E07490: Function Must Return Value
Classification: FUNCTION_MUST_RETURN_VALUE
Description
The function must return a value for use in this stream operation context. Stream operations like 'call' and 'async' require functions that produce return values which become the stream output. Functions without return values (void/procedure-like functions) cannot be used in these contexts because the stream has no data to pass to subsequent operations. The function must declare a return type using the `<- rtn as Type` syntax. If side effects are needed without producing stream values, consider using different stream termination operations.
Example (Error)
#!ek9
defines module bad.streams5
defines function
doesNotReturnAnything()
value <- 1
assert value?
BrokenStreamCatCall3()
collector <- StringCollector()
@Error: FULL_RESOLUTION: FUNCTION_MUST_RETURN_VALUE
cat [doesNotReturnAnything] | call > collector
assert collector?
Solution
#!ek9
defines function
process()
-> value as Integer
<- result as Boolean: true //Return value declared
stdout.println(value)
//Or use in expression
transform()
-> value as Integer
<- result as String: String(value * 2)
See Also
E07520: Must Return Boolean
Classification: MUST_RETURN_BOOLEAN
Description
The ? (isSet) operator must return a Boolean value because it represents
a fundamental query: "is this object set/initialized with a meaningful value?" The
compiler enforces this return type during explicit type symbol definition to ensure
the operator integrates correctly with EK9's tri-state object model (absent/unset/set).
Returning any type other than Boolean would break conditional logic that depends on
checking object state. This operator is used extensively in guards, assertions, and
control flow, making the Boolean return type non-negotiable.
Example (Error)
#!ek9
defines module bad.classes.operators.examples2
defines class
C2
//The 'is set' operator
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: MUST_RETURN_BOOLEAN
override operator ? as pure
<- rtn as Integer: 1
Solution
#!ek9
defines module bad.classes.operators.examples2
defines class
C2
//The 'is set' operator must return Boolean
override operator ? as pure
<- rtn as Boolean: true
See Also
E07530: Only Compatible With Boolean
Classification: ONLY_COMPATIBLE_WITH_BOOLEAN
Description
Certain language constructs like assert require Boolean expressions
because they fundamentally test true/false conditions. While EK9 allows checking if
a value is set using the ? operator (assert int1?), directly
asserting a non-Boolean type (assert int1) is not permitted. The compiler
detects this during full resolution to prevent logical errors where developers might
confuse value testing with Boolean conditions. Use the ? operator to
convert non-Boolean values to Boolean for assertion.
Example (Error)
#!ek9
defines module bad.range.collection
defines program
CheckAssert()
int1 <- 1
boolean1 <- true
boolean2 <- false
//All of these will be fine.
assert boolean1
assert boolean2
assert int1?
//But this won't be fine, assert needs a boolean to check.
@Error: FULL_RESOLUTION: ONLY_COMPATIBLE_WITH_BOOLEAN
assert int1
Solution
#!ek9
defines module bad.range.collection
defines program
CheckAssert()
int1 <- 1
boolean1 <- true
//Use ? operator to convert to Boolean
assert int1? //Checks if int1 is set
assert boolean1 //Direct Boolean assertion
See Also
E07540: Must Be A Boolean
Classification: MUST_BE_A_BOOLEAN
Description
The ternary operator (<- condition <- value1
else value2) requires a Boolean control expression to determine which
value to select. Using a non-Boolean type like String as the control expression is
not permitted. The compiler detects this during full resolution because the ternary
fundamentally performs conditional selection based on true/false evaluation. Unlike
languages with truthy/falsy semantics, EK9 requires explicit Boolean types for
conditional logic to prevent ambiguous behavior.
Example (Error)
#!ek9
defines module bad.control.types
defines program
TernaryWithNonBooleanControl()
stringControl <- "Not Allowed"
@Error: FULL_RESOLUTION: MUST_BE_A_BOOLEAN
result1 <- stringControl <- "A" else "B"
assert result1?
Solution
#!ek9
defines module bad.control.types
defines program
TernaryWithBooleanControl()
booleanControl <- true
//Use Boolean control expression
result1 <- booleanControl <- "A" else "B"
assert result1?
See Also
E07550: Must Return Integer
Classification: MUST_RETURN_INTEGER
Description
The spaceship comparison operator (<=>) must return an Integer
to indicate ordering: negative for less-than, zero for equal, positive for greater-than.
This three-way comparison result enables sorting algorithms and ordered collections.
The compiler enforces this return type during explicit type symbol definition because
the operator's semantic contract requires numeric ordering values. Returning any other
type (like Boolean or the class itself) would break sorting and comparison logic that
depends on the integer ordering convention.
Example (Error)
#!ek9
defines module bad.classes.operators.examples1
defines class
C1
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: MUST_RETURN_INTEGER
operator <=> as pure
-> arg0 as C1
<- rtn as C1: this
Solution
#!ek9
defines module bad.classes.operators.examples1
defines class
C1
operator <=> as pure
-> arg0 as C1
<- rtn as Integer: 0 //Returns -1, 0, or 1 for ordering
See Also
E07560: Must Be Integer Greater Than Zero
Classification: MUST_BE_INTEGER_GREATER_THAN_ZERO
Description
Stream operations like head, tail, and skip
require an Integer argument greater than zero because taking zero elements or skipping
zero elements is semantically meaningless. The compiler detects this during full
resolution to prevent logic errors. Negative values are also rejected because these
operations work forward through streams - you cannot take or skip a negative count
of elements. Use positive integers (1, 2, 3...) to specify how many elements to
process.
Example (Error)
#!ek9
defines module bad.stream.headtailskip
defines program
InvalidStreamCatHead1()
collection <- List() of String
@Error: FULL_RESOLUTION: MUST_BE_INTEGER_GREATER_THAN_ZERO
cat ["last", "first"] | head 0 > collection
assert collection?
Solution
#!ek9
defines module bad.stream.headtailskip
defines program
ValidStreamCatHead()
collection <- List() of String
cat ["last", "first"] | head 1 > collection //Must be >= 1
assert collection?
See Also
E07570: Must Return String
Classification: MUST_RETURN_STRING
Description
The string representation operator ($) must return a String because it
converts objects to their textual representation for display, logging, or serialization.
The compiler enforces this return type during explicit type symbol definition to ensure
the operator integrates correctly with string interpolation ("Value: $obj"),
concatenation, and output operations. Returning any other type (like Integer or custom
objects) would break string formatting that depends on the operator producing text.
Example (Error)
#!ek9
defines module bad.classes.operators.examples4
defines class
C4
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: MUST_RETURN_STRING
operator $ as pure
<- rtn as Integer: 1
Solution
#!ek9
defines module bad.classes.operators.examples4
defines class
C4
operator $ as pure
<- rtn as String: "C4 instance" //Returns String representation
See Also
E07580: Must Return JSON
Classification: MUST_RETURN_JSON
Description
The JSON representation operator ($$) must return a JSON type because it
converts objects to structured JSON format for APIs, configuration files, and data
interchange. The compiler enforces this return type during explicit type symbol definition
to ensure the operator produces valid JSON structures that can be serialized, transmitted,
and parsed. Returning String (even if it contains JSON text) is not permitted - the
operator must return EK9's JSON type which provides structural guarantees and proper
encoding.
Example (Error)
#!ek9
defines module bad.classes.operators.examples4
defines class
C4
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: MUST_RETURN_JSON
operator $$ as pure
<- rtn as String: ""
Solution
#!ek9
defines module bad.classes.operators.examples4
defines class
C4
operator $$ as pure
<- rtn as JSON: JSON() //Returns JSON structure
See Also
E07590: Program Can Only Return Integer
Classification: PROGRAM_CAN_ONLY_RETURN_INTEGER
Description
EK9 programs (entry points) can only return Integer types, following the Unix/POSIX convention where programs communicate success or failure through integer exit codes (0 for success, non-zero for errors). The compiler enforces this during early symbol definition to prevent programs from returning incompatible types like Float, String, Boolean, or custom objects. This restriction enables proper integration with operating system process management and shell scripting, where exit codes must be integers. Programs that don't need to return a value can simply omit the return declaration.
Example (Error)
#!ek9
defines module bad.programs.returnparams
defines program
//Failure 1 - cannot return anything other than an Integer
BadReturnType1()
@Error: SYMBOL_DEFINITION: PROGRAM_CAN_ONLY_RETURN_INTEGER
<- rtn as Float: 0.9
//Just some body to stop any other compiler errors
v <- "Steve"
assert v?
Solution
#!ek9
defines module bad.programs.returnparams
defines program
GoodReturnType()
<- rtn as Integer: 0 //Success exit code
v <- "Steve"
assert v?
See Also
E07600: Program Argument Type Invalid
Classification: PROGRAM_ARGUMENT_TYPE_INVALID
Description
Program arguments (command-line parameters) must use EK9 built-in types that can be easily parsed from string representations - String, Integer, Boolean, Float, Date, etc. Custom types like records, classes, functions, or complex data structures are not permitted as program arguments. The compiler enforces this during symbol definition because program arguments come from the command line as text and must be convertible from strings. Complex types would require custom parsing logic that belongs in the program body, not in the parameter list.
Example (Error)
#!ek9
defines module bad.programs.argumentschecks
defines record
ATestRecord
first <- "A"
second <- 9
defines program
//Failure2 - not supported, developer must take the List of String and build/parse them to create the record.
BadArgumentType1()
@Error: SYMBOL_DEFINITION: PROGRAM_ARGUMENT_TYPE_INVALID
-> arg as ATestRecord
//Just some body to stop any other compiler errors
v <- "Steve"
assert v?
Solution: Use Built-in Types
#!ek9
defines module bad.programs.argumentschecks
defines program
GoodArgumentType()
->
firstName as String
age as Integer
//Build custom type from built-in arguments
record <- ATestRecord(firstName, age)
assert record?
See Also
E07610: Program Arguments Inappropriate
Classification: PROGRAM_ARGUMENTS_INAPPROPRIATE
Description
Program arguments must be either individual named parameters OR a single
List of String for raw command-line access - mixing both is inappropriate.
The compiler enforces this during symbol definition because these two approaches represent
different argument processing strategies. Individual parameters (like port as Integer)
provide type-safe, structured access. A single List of String provides raw
access to all command-line arguments. Combining them creates ambiguous semantics about
which arguments go where.
Example (Error)
#!ek9
defines module bad.programs.argumentschecks
defines program
//Failure1 - not supported, it's either list all the parameters or have a List of String
BadArgumentCombination()
->
arg1 as Integer
@Error: SYMBOL_DEFINITION: PROGRAM_ARGUMENTS_INAPPROPRIATE
arg2 as List of String
Solution 1: Use Individual Parameters
#!ek9
defines module bad.programs.argumentschecks
defines program
GoodIndividualParams()
->
port as Integer
host as String
debug as Boolean
Solution 2: Use Raw Argument List
#!ek9
defines module bad.programs.argumentschecks
defines program
GoodRawArgs()
-> args as List of String
//Parse args manually as needed
See Also
E07860: Function Or Delegate Required
Classification: FUNCTION_OR_DELEGATE_REQUIRED
Description
A function or function delegate is required. Operations like map, filter, or reduce require functional arguments.
Example (Error)
#!ek9
defines function
demo()
numbers <- [1, 2, 3]
@Error: FULL_RESOLUTION: FUNCTION_OR_DELEGATE_REQUIRED
result <- cat numbers | map with 42 //Not a function
Solution
#!ek9
defines function
doubler()
-> value as Integer
<- result as Integer: value * 2
defines function
demo()
numbers <- [1, 2, 3]
result <- cat numbers | map with doubler | collect as List of Integer
See Also
E07870: Integer Var Or Function Or Delegate Required
Classification: INTEGER_VAR_OR_FUNCTION_OR_DELEGATE_REQUIRED
Description
An Integer value or function/function delegate is required. Some operations accept either a literal integer or a function that produces integers.
Example (Error)
#!ek9
defines function
demo()
@Error: FULL_RESOLUTION: INTEGER_VAR_OR_FUNCTION_OR_DELEGATE_REQUIRED
result <- someOperation("not an integer") //Need Integer or function
Solution 1: Use Integer Value
#!ek9
defines function
demo()
result <- someOperation(42) //Integer value
Solution 2: Use Function
#!ek9
defines function
getSize()
<- result as Integer: 100
defines function
demo()
result <- someOperation(getSize) //Function that returns Integer
See Also
E07880: Function Or Delegate Not Required
Classification: FUNCTION_OR_DELEGATE_NOT_REQUIRED
Description
A function or function delegate is not required here. The operation expects a value, not a function reference.
Example (Error)
#!ek9
defines function
getValue()
<- result as Integer: 42
defines function
demo()
@Error: FULL_RESOLUTION: FUNCTION_OR_DELEGATE_NOT_REQUIRED
value <- 10 + getValue //Should call the function
Solution: Call the Function
#!ek9
defines function
getValue()
<- result as Integer: 42
defines function
demo()
value <- 10 + getValue() //Call the function
//Or use the value directly
directValue <- 52
See Also
Method and Modifier Errors
Method and modifier errors relate to method overriding, abstract methods, constructors, access modifiers, default operators, and implementation requirements. These ensure proper object-oriented design and inheritance in EK9.
E07010: Method Access Modifier Private Override
Classification: METHOD_ACCESS_MODIFIER_PRIVATE_OVERRIDE
Description
Combining 'private' with 'override' is contradictory and not allowed. Private methods are not visible to subclasses, making it impossible for them to override parent methods. The 'override' modifier requires method visibility (public, protected, or package-private) to enable polymorphic behavior. Additionally, if a parent method is private, it is not visible to subclasses and therefore cannot be overridden. This error is caught during symbol definition phase to prevent logical contradictions in method access semantics.
Example (Error)
#!ek9
defines module bad.classmodifier.use
defines class
C0
private basicMethod()
<- rtn as String: "OK"
C1 extends C0
//Attempting to override a private method from parent
@Error: SYMBOL_DEFINITION: METHOD_ACCESS_MODIFIER_PRIVATE_OVERRIDE
override private basicMethod()
<- rtn as String: "OK"
Solution: Remove Private or Override
#!ek9
defines class extends Base
Derived
//Option 1: Override without private
override calculate()
<- result as Integer: 100
//Option 2: Private without override (different method)
private internalCalculate()
<- result as Integer: 100
See Also
- Method Override
- Access Modifiers
E07020: Override And Abstract
Classification: OVERRIDE_AND_ABSTRACT
Description
Combining 'override' with 'abstract' is logically contradictory. The 'override' modifier indicates that a method is providing a concrete implementation for a parent class or trait method. The 'abstract' modifier indicates a method declaration without implementation, requiring subclasses to provide the implementation. A method cannot simultaneously provide an implementation (override) and declare it lacks an implementation (abstract). This applies to both regular methods and operators. The compiler enforces this during explicit type symbol definition to prevent logical inconsistencies in type hierarchies.
Example (Error)
#!ek9
defines module bad.abstractuse.example
defines class
C2 as abstract
operator > as pure abstract
-> arg0 as C2
<- rtn as Boolean?
//Attempting to override an operator but also declaring it abstract
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: OVERRIDE_AND_ABSTRACT
override operator <= as pure abstract
-> arg0 as C2
<- rtn as Boolean?
Solution: Choose One
#!ek9
defines class extends Base
Derived
//Option 1: Provide implementation (override)
override process()
<- result as String: "Processed"
//Option 2: Keep abstract (no override)
abstract
process()
<- result as String
See Also
E07030: Default And Trait
Classification: DEFAULT_AND_TRAIT
Description
The 'default' operator keyword is not supported on traits. Default operators use compiler-generated implementations based on the type's structure (e.g., generating comparison operators from a `<=>` implementation). This feature requires concrete type information including property layouts and initialization semantics, which traits do not possess. Traits are abstract contracts that define behavior without implementation details. For operators on traits, you must either provide explicit implementations or declare them as abstract. This error is caught early during symbol definition phase to prevent invalid trait definitions.
Example (Error)
#!ek9
defines module earlybad.defaultoperators.examples
defines trait
T1
operator <=> as pure
-> arg0 as T1
<- rtn as Integer: 0
//Attempting to use default operator generation on a trait
@Error: SYMBOL_DEFINITION: DEFAULT_AND_TRAIT
default operator >=
Solution: Provide Full Implementation
#!ek9
defines trait
Comparable
operator == as pure //Provide implementation
-> arg0 as Comparable
<- result as Boolean
//Implementation logic
See Also
E07040: Trait By Identifier Not Supported
Classification: TRAIT_BY_IDENTIFIER_NOT_SUPPORTED
Description
The 'by' delegation identifier is not supported when composing traits. Delegation using 'by' is a class-specific feature that forwards method calls to a contained property implementing a trait. Traits themselves cannot have properties (they're purely behavioral contracts), so they cannot delegate to property instances. When composing a trait from multiple other traits using `with trait of T1, T2`, you cannot specify delegation identifiers. Classes implementing the composite trait can use delegation, but trait definitions cannot. This ensures traits remain pure behavioral abstractions without state.
Example (Error)
#!ek9
defines module trait.with.trait.by
defines trait
T1
methodOne()
<- rtn <- true
T2
methodTwo()
<- rtn <- false
//Traits cannot delegate - they have no properties
@Error: FULL_RESOLUTION: TRAIT_BY_IDENTIFIER_NOT_SUPPORTED
Composite with trait of T1 by t1, T2
Solution: Use in Class
#!ek9
defines trait
Printable
print()
-> value as String
defines class with trait of Printable
Document
printer as Printer: Printer()
by printer //'by' delegation in class
See Also
E07050: Abstract Constructor
Classification: ABSTRACT_CONSTRUCTOR
Description
Declaring a constructor as 'abstract' is logically contradictory and not allowed. Constructors are responsible for initializing new object instances, which requires concrete implementation code to set initial property values and establish object state. Abstract declarations indicate "no implementation provided, subclasses must implement", but constructors are not inherited in the same way as methods - each class must provide its own initialization logic. The concept of an abstract constructor makes no sense because you cannot create instances of a type without concrete initialization code. This error is caught during symbol definition phase to prevent fundamentally flawed type definitions.
Example (Error)
#!ek9
defines module bad.classmodifier.use
defines class
C1
p1 as String?
p2 as Integer?
//Constructors cannot be abstract - they must initialize objects
@Error: SYMBOL_DEFINITION: ABSTRACT_CONSTRUCTOR
C1() as abstract
-> param as Integer
Solution: Provide Implementation
#!ek9
defines class
MyClass
value as Integer: 0
MyClass() //Remove abstract
-> initialValue as Integer
value := initialValue
See Also
- Constructors
- Abstract Classes
E07060: Override Constructor
Classification: OVERRIDE_CONSTRUCTOR
Description
Using 'override' modifier on constructors is not allowed. While it's natural for developers from other object-oriented languages to think they are "overriding" a parent constructor, EK9 does not express constructor inheritance this way. Constructors are not inherited like methods - each class defines its own initialization logic. Even when calling `super()` to initialize the parent class, you are not overriding the parent constructor, you are delegating initialization to it. The 'override' modifier is specifically for methods and operators that participate in polymorphism, which does not apply to constructors. This error is caught during symbol definition to prevent conceptual confusion about constructor semantics.
Example (Error)
#!ek9
defines module bad.classmodifier.use
defines class
C1
p1 as String?
p2 as Integer?
//Cannot use override on constructors
@Error: SYMBOL_DEFINITION: OVERRIDE_CONSTRUCTOR
override C1()
->
p1 as String
p2 as Integer
super()
this.p1 = p1
this.p2 = p2
Solution: Remove Override
#!ek9
defines class extends BaseClass
DerivedClass
DerivedClass() //No 'override' needed
super()
See Also
E07070: Traits Do Not Have Constructors
Classification: TRAITS_DO_NOT_HAVE_CONSTRUCTORS
Description
Traits cannot have constructors. Traits are pure behavioral contracts that define method signatures and optionally provide default implementations. Unlike classes, traits cannot be directly instantiated - they must be implemented by classes or other aggregates. Since traits cannot create instances, constructor methods make no sense and are not allowed. Initialization logic belongs in the classes that implement the trait, not in the trait itself. This restriction ensures traits remain focused on defining behavior without concerning themselves with object instantiation. This error is caught during symbol definition to prevent invalid trait definitions.
Example (Error)
#!ek9
defines module bad.classmodifier.use
defines trait
T1
//Traits cannot have constructors
@Error: SYMBOL_DEFINITION: TRAITS_DO_NOT_HAVE_CONSTRUCTORS
T1()
Solution: Use Initialization Methods
#!ek9
defines trait
Nameable
name as String: ""
setName() //Use method instead
-> newName as String
name := newName
See Also
E07080: Invalid Default Constructor
Classification: INVALID_DEFAULT_CONSTRUCTOR
Description
The 'default' constructor modifier can only be used on parameterless constructors. In EK9, 'default' has a specific meaning: it marks the zero-argument constructor that can be automatically invoked during object initialization. A constructor with parameters cannot be a default constructor because there would be no way to automatically determine what argument values to provide. If you need a constructor with parameters, define it as a regular (non-default) constructor. The compiler enforces this during symbol definition to ensure clear and unambiguous object initialization semantics.
Example (Error)
#!ek9
defines module bad.classmodifier.use
defines class
C1
//Default constructor cannot have parameters
@Error: SYMBOL_DEFINITION: INVALID_DEFAULT_CONSTRUCTOR
default C1()
-> param as String
Solution
#!ek9
defines class
MyClass
value as Integer: 0
default MyClass() //No parameters for default
MyClass() //Regular constructor with parameters
-> initialValue as Integer
value := initialValue
See Also
- Constructors
- Default Constructor
E07090: Default Only For Constructors
Classification: DEFAULT_ONLY_FOR_CONSTRUCTORS
Description
The 'default' modifier is only valid for constructors and operators, not regular methods. For constructors, 'default' marks the parameterless constructor that can be automatically invoked. For operators, 'default' requests compiler-generated implementations based on other operators (e.g., generating `>=` from `<=>`). Regular methods must be explicitly implemented and cannot use 'default'. This restriction prevents confusion about what 'default' would mean for a method (there is no "default implementation" that can be automatically generated for arbitrary methods). This error is caught during symbol definition to ensure proper modifier usage.
Example (Error)
#!ek9
defines module bad.default.methods.examples
defines class
C1
//Valid: default constructor
default C1()
//Invalid: default on regular method
@Error: SYMBOL_DEFINITION: DEFAULT_ONLY_FOR_CONSTRUCTORS
default normalMethod()
-> arg0 as String
<- rtn as String: String(arg0)
Solution: Remove Default
#!ek9
defines class
MyClass
process() //Remove 'default'
<- result as String: "Processed"
default MyClass() //'default' OK for constructors
See Also
E07100: Abstract But Body Provided
Classification: ABSTRACT_BUT_BODY_PROVIDED
Description
A method or operator declared as 'abstract' cannot have an implementation body. The 'abstract' modifier means "this declares a contract that subclasses must implement" - it explicitly indicates no implementation is provided. Providing a body (return value initialization or method logic) contradicts this declaration. This error applies to both regular methods and operators. If you want to provide an implementation, remove the 'abstract' modifier. If you want to require subclasses to implement it, remove the body. This error is caught during explicit type symbol definition when the compiler validates operator and method signatures.
Example (Error)
#!ek9
defines module bad.abstractuse.example
defines class
C2 as abstract
//Declaring operator as abstract but providing implementation
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: ABSTRACT_BUT_BODY_PROVIDED
operator < as pure abstract
-> arg0 as C2
<- rtn as Boolean: true
Solution: Remove Abstract or Body
#!ek9
defines class
MyClass
//Option 1: Remove abstract, keep implementation
process()
<- result as String: "Implementation"
//Option 2: Keep abstract, remove body
abstract
calculate()
<- result as Integer
See Also
E07110: Not Abstract And No Body Provided
Classification: NOT_ABSTRACT_AND_NO_BODY_PROVIDED
Description
A method or operator without an implementation body must be explicitly declared as 'abstract'. In EK9, every method and operator must either provide a concrete implementation (body) or be marked abstract to indicate that subclasses must provide the implementation. Unlike traits (which are implicitly abstract), class methods and operators must be explicit about their abstract nature. This prevents accidental omissions where a developer forgot to implement a method - the compiler requires you to consciously declare it abstract if no body is provided. This error is caught during explicit type symbol definition when validating operator and method completeness.
Example (Error)
#!ek9
defines module bad.abstractuse.example
defines class
C2 as abstract
//Operator without body must be declared abstract
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: NOT_ABSTRACT_AND_NO_BODY_PROVIDED
operator >= as pure
-> arg0 as C2
<- rtn as Boolean?
Solution
#!ek9
defines class
MyClass
//Option 1: Provide implementation
process()
<- result as String: "Processed"
//Option 2: Declare as abstract
abstract
calculate()
<- result as Integer
See Also
E07120: Dispatcher But No Body Provided
Classification: DISPATCHER_BUT_NO_BODY_PROVIDED
Description
Dispatcher methods must provide a base implementation body. In EK9, 'dispatcher' methods use dynamic method resolution to call different method overloads based on actual parameter types at runtime. Unlike abstract methods which defer implementation to subclasses, dispatcher methods must provide a fallback implementation that executes when no specific overload matches the runtime types. This base implementation serves as the entry point that dispatches to specialized methods, or handles cases where no specialized method exists. The requirement for a body ensures dispatchers are complete and functional. This error is caught during symbol definition to enforce proper dispatcher structure.
Example (Error)
#!ek9
defines module bad.classes.examples
defines class
BadDispatcher1
//Dispatcher method without implementation body
@Error: SYMBOL_DEFINITION: DISPATCHER_BUT_NO_BODY_PROVIDED
process() as dispatcher
Solution: Provide Implementation
#!ek9
defines class
RequestHandler
@Dispatcher
process()
-> request
<- result as Boolean: true //Provide default implementation
handleGet()
-> request as GetRequest
<- result as Boolean: true
handlePost()
-> request as PostRequest
<- result as Boolean: true
See Also
E07130: Not Marked Abstract But Is Abstract
Classification: NOT_MARKED_ABSTRACT_BUT_IS_ABSTRACT
Description
A class that contains abstract methods or operators must be explicitly marked as 'abstract'. When a class implements a trait with abstract members, or defines abstract members itself, it becomes abstract and cannot be directly instantiated. EK9 requires you to explicitly declare the class as abstract to make this clear to developers using the class. This prevents accidental attempts to instantiate classes that lack complete implementations. This error is caught during full resolution after all traits, inheritance, and method overrides are fully analyzed.
Example (Error)
#!ek9
defines module bad.overridden.classoperators
defines trait
T1
operator $ as pure abstract
<- rtn as String?
//Class implements trait with abstract operator but is not marked abstract
@Error: FULL_RESOLUTION: NOT_MARKED_ABSTRACT_BUT_IS_ABSTRACT
C2 with trait of T1
operator #? as pure
<- rtn as Integer: 0
default operator ?
Solution: Mark Class as Abstract
#!ek9
defines class
abstract MyClass //Add 'abstract'
abstract
process()
<- result as String
See Also
E07140: Dynamic Class Must Implement Abstracts
Classification: DYNAMIC_CLASS_MUST_IMPLEMENT_ABSTRACTS
Description
Dynamic classes must provide implementations for all abstract methods and operators from their base classes and traits. When you create a dynamic class using `() with trait of T as class` or extend an abstract class, EK9 requires complete implementation of all abstract members at the point of creation. Unlike static classes where abstract members can be inherited and implemented later in subclasses, dynamic classes are concrete instances that must be immediately usable. This ensures type safety and prevents runtime errors from calling unimplemented methods. This error is caught during full resolution after analyzing all traits and method requirements.
Example (Error)
#!ek9
defines module fuzz.dynamic.missing.trait.methods
defines trait
Processor
process() as abstract
-> input as String
<- output as String?
validate() as abstract
-> input as String
<- valid as Boolean?
defines program
TestMissingTraitMethods()
//Missing implementation of validate() method
@Error: FULL_RESOLUTION: DYNAMIC_CLASS_MUST_IMPLEMENT_ABSTRACTS
processor <- () with trait of Processor as class
override process()
-> input as String
<- output as String: "processed: " + input
//validate() method not implemented - ERROR
Solution: Implement All Abstracts
#!ek9
defines function
demo()
instance <- BaseClass() with
value as Integer: 42
override process() //Implement abstract method
<- result as String: "Processed"
See Also
- Dynamic Classes
- Abstract Classes
E07150: Text Method Missing
Classification: TEXT_METHOD_MISSING
Description
When defining text constructs for multiple language locales, all locales must have identical method signatures. EK9's text system allows you to define locale-specific text methods using `defines text for "en_GB"` and `defines text for "de"` for example. Every method defined in one locale must exist with the same signature (parameters and return type) in all other locales. This ensures that code can call text methods without knowing which locale is active - all locales provide the same interface. Missing methods cause runtime errors when the locale switches, so the compiler enforces completeness. This error is caught during full resolution after analyzing all text construct definitions.
Example (Error)
#!ek9
defines module bad.missingtextmethods.examples1
defines text for "en_GB"
//Missing namedWelcome(name as String) method present in German locale
@Error: FULL_RESOLUTION: TEXT_METHOD_MISSING
WelcomePageText
namedWelcome()
-> person as Person
`Welcome ${person.firstName}`
defines text for "de"
WelcomePageText
namedWelcome()
-> person as Person
`Willkommen ${person.firstName}`
//This method exists in German but not in English
namedWelcome()
-> name as String
`Willkommen ${name}`
Solution: Add Missing Method to All Locales
#!ek9
defines module bad.missingtextmethods.examples1
defines text for "en_GB"
WelcomePageText
namedWelcome()
-> person as Person
`Welcome ${person.firstName}`
//Add the missing method that exists in German locale
namedWelcome()
-> name as String
`Welcome ${name}`
defines text for "de"
WelcomePageText
namedWelcome()
-> person as Person
`Willkommen ${person.firstName}`
namedWelcome()
-> name as String
`Willkommen ${name}`
See Also
E07160: Implementation Must Be Provided
Classification: IMPLEMENTATION_MUST_BE_PROVIDED
Description
Certain EK9 constructs must always have complete implementations and cannot be abstract or unimplemented. Programs (main entry points), functions, and service methods must all have bodies. Unlike classes which can have abstract methods, these constructs are executable entry points that must provide concrete behavior. A program without a body cannot run, a function without implementation cannot be called, and a service without implementation cannot handle HTTP requests. This restriction ensures all callable constructs are complete and functional. This error is caught during symbol definition to prevent fundamentally incomplete definitions.
Example (Error)
#!ek9
defines module bad.programs.examples
defines program
//Programs must have implementation bodies
@Error: SYMBOL_DEFINITION: IMPLEMENTATION_MUST_BE_PROVIDED
Program4()
Solution: Provide Implementation
#!ek9
defines function
calculate()
-> value as Integer
<- result as Integer: value * 2 //Provide implementation
See Also
E07170: Explicit Constructor Required
Classification: EXPLICIT_CONSTRUCTOR_REQUIRED
Description
An explicit constructor must be provided when a class has properties that are never initialized. EK9 requires all properties to be initialized either through inline initialization (`prop <- value`), or through constructor initialization. When properties are declared without inline values and are not initialized in any constructor, the compiler cannot generate safe initialization code. You must provide an explicit constructor that initializes all such properties. This ensures object instances are never in an invalid state with uninitialized properties. This error is caught during pre-IR checks after analyzing all property initialization flows.
Example (Error)
#!ek9
defines module complexity.class.never.init
defines class
//Class with uninitialized property requires explicit constructor
@Error: PRE_IR_CHECKS: EXPLICIT_CONSTRUCTOR_REQUIRED
ClassWithUninitializedProps
@Error: PRE_IR_CHECKS: NEVER_INITIALISED
prop1 as String?
useProperty()
@Error: PRE_IR_CHECKS: NOT_INITIALISED_BEFORE_USE
result <- prop1 + " suffix"
assert result?
Solution: Provide Constructor That Initializes Properties
#!ek9
defines module complexity.class.never.init
defines class
ClassWithUninitializedProps
prop1 as String?
//Constructor initializes the uninitialized property
ClassWithUninitializedProps()
-> initialValue as String
prop1 :=? initialValue //Initialize property in constructor
useProperty()
result <- prop1 + " suffix"
assert result?
See Also
E07180: Missing Operator In This
Classification: MISSING_OPERATOR_IN_THIS
Description
When using 'default operator' to auto-generate operator implementations, this type must already define certain foundational operators. For example, defaulting comparison operators (`<`, `<=`, `>`, `>=`) requires the `<=>` operator to be defined or also defaulted. The compiler uses existing operator implementations to generate the requested default operators. If the required foundation operators are missing from this type, default generation cannot proceed. You must either provide the missing operator implementation yourself, or also default it if possible. This ensures all generated operators have valid implementations. This error is caught during full resolution when analyzing operator dependencies.
Example (Error)
#!ek9
defines module bad.defaulted.classoperators
defines class
SomeClass2
prop1 <- Time()
//Cannot default < operator without <=> operator
@Error: FULL_RESOLUTION: MISSING_OPERATOR_IN_THIS
default operator <
Solution: Define the Operator
#!ek9
defines class
MyClass
value as Integer: 0
operator == as pure
-> arg0 as MyClass
<- result as Boolean: value == arg0.value
default operator <> //Now can use default
See Also
E07190: Missing Operator In Super
Classification: MISSING_OPERATOR_IN_SUPER
Description
When using 'default operator' in a subclass, the parent class must define the operator being defaulted. Default operator generation in subclasses first delegates to the parent class's operator implementation, then applies the operator to the subclass's additional properties. This ensures inherited properties are properly included in operator behavior (e.g., equality checking all properties including inherited ones). If the parent class lacks the required operator, default generation cannot create a complete implementation. You must add the operator to the parent class, or provide an explicit implementation in the subclass. This error is caught during full resolution when analyzing inheritance hierarchies and operator availability.
Example (Error)
#!ek9
defines module bad.defaulted.classoperators
defines class
MinimalBase1 as open
prop1 <- String()
//Parent class lacks operators needed for default generation
Invalid1 is MinimalBase1
prop2 <- String()
@Error: FULL_RESOLUTION: MISSING_OPERATOR_IN_SUPER
default operator
Solution: Define in Parent
#!ek9
defines class
Base
operator == as pure
-> arg0 as Base
<- result as Boolean: true
defines class extends Base
Derived
default operator == //Now valid
See Also
E07200: Missing Operator In Property Type
Classification: MISSING_OPERATOR_IN_PROPERTY_TYPE
Description
When using 'default operator' to generate implementations based on properties, all property types must define the operator being defaulted. Default operator generation works by applying the operator to each property in order of definition. For example, defaulting the `==` operator generates code that compares each property using `==`. If any property's type lacks the required operator, the generated code would be invalid. You must ensure all property types implement the necessary operators before using default operator generation. This error is caught during full resolution when analyzing property types and their available operators.
Example (Error)
#!ek9
defines module bad.defaulted.classoperators
defines class
SomeClass1
prop1 <- Time()
//No operators defined
//Property type SomeClass1 lacks operators for default generation
Invalid3
prop1 <- SomeClass1()
@Error: FULL_RESOLUTION: MISSING_OPERATOR_IN_PROPERTY_TYPE
default operator
Solution: Define Operator in Property Type
#!ek9
defines class
Inner
operator == as pure
-> arg0 as Inner
<- result as Boolean: true
defines class
Outer
inner as Inner: Inner()
default operator == //Now valid
See Also
E07210: Function Delegate With Default Operators
Classification: FUNCTION_DELEGATE_WITH_DEFAULT_OPERATORS
Description
Records containing function delegate properties cannot use default operators for comparison operators like `<=>`. Function delegates are references to executable code, not data values. Comparing function delegates for ordering (`<=>`) has no meaningful semantic definition - there's no natural ordering of function references. Default operator generation relies on property-by-property comparison, but function delegates don't support comparison operations. If a record needs comparison operators and contains function delegates, those operators must be explicitly implemented to compare non-delegate properties only. This ensures comparisons have clear semantics.
Example (Error)
#!ek9
defines module functiondelegate.inrecord.withgeneric
defines record
R1
delegate as TestFunction?
default operator ?
//Cannot default <=> operator with function delegate property
@Error: FULL_RESOLUTION: FUNCTION_DELEGATE_WITH_DEFAULT_OPERATORS
default operator <=>
Solution: Define Explicitly
#!ek9
defines function
MyFunction
operator == as pure //Define explicitly
-> arg0 as MyFunction
<- result as Boolean: true
See Also
E07220: Operator Default Not Supported
Classification: OPERATOR_DEFAULT_NOT_SUPPORTED
Description
Only specific operators support default generation - primarily comparison and equality operators that can be derived from the `<=>` operator, along with `?`, `$`, `$$`, and `#?`. Operators like `#<`, `#>`, `empty`, `contains`, `and`, `or`, and `xor` have semantics that cannot be automatically generated from property comparisons. These operators require custom logic specific to each type's behavior. For example, `contains` checks membership which is type-specific. The compiler restricts default generation to operators with well-defined automatic implementations. This is enforced during symbol definition to prevent invalid operator declarations.
Example (Error)
#!ek9
defines module earlybad.defaultoperators.examples
defines record
R1
property1 <- String()
//Operator 'empty' cannot be defaulted
@Error: SYMBOL_DEFINITION: OPERATOR_DEFAULT_NOT_SUPPORTED
default operator empty
Solution: Provide Implementation
#!ek9
defines class
MyClass
value as Integer: 0
operator ++ //Implement explicitly
value++
See Also
E07230: Default With Operator Signature
Classification: DEFAULT_WITH_OPERATOR_SIGNATURE
Description
The 'default' keyword for operators must appear alone without modifiers, parameters, return types, or body. Default operators request compiler-generated implementations based on existing operators and properties. Providing a signature (parameters, return type) or modifiers (pure, abstract) contradicts the purpose of 'default' - you're either asking the compiler to generate it (default) or providing it yourself (full signature). Mixing both creates ambiguity about whether to use your signature or generate one. The compiler requires clear intent: either `default operator X` for generation, or a complete operator definition. This is enforced during symbol definition to prevent ambiguous operator declarations.
Example (Error)
#!ek9
defines module earlybad.defaultoperators.examples
defines record
R1
property1 <- String()
//Default operator cannot have signature elements
@Error: SYMBOL_DEFINITION: DEFAULT_WITH_OPERATOR_SIGNATURE
default operator <= as pure
-> arg0 as R1
<- rtn as Boolean: false
Solution: Use Default Without Signature
#!ek9
defines class
MyClass
value as Integer: 0
operator == as pure //Define fully
-> arg0 as MyClass
<- result as Boolean: value == arg0.value
default operator <> //Just default, no signature
See Also
E07240: Method Modifier Protected In Service
Classification: METHOD_MODIFIER_PROTECTED_IN_SERVICE
Description
Protected access modifier is not valid in services. Services are web service endpoints where all operations must be either publicly accessible (for HTTP endpoints) or private (for internal helper methods). The protected modifier, designed for class inheritance, has no meaningful application in service context since services cannot be extended. Only private and public modifiers are supported.
Example (Error)
#!ek9
defines module bad.duplicate.and.modifier.service.methods
defines service
S4 for :/site4
//This is OK
private index1()
<- rtn <- 0
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: METHOD_MODIFIER_PROTECTED_IN_SERVICE
protected index2()
<- rtn <- 0
Solution: Use Public or Private
#!ek9
defines service
MyService
GET for "/api/data"
<- result as HTTPResponse: HTTPResponse().ok("Data")
private helper() //Use private
<- result as String: "Helper"
See Also
E07250: Method Modifier Protected In Component
Classification: METHOD_MODIFIER_PROTECTED_IN_COMPONENT
Description
Protected access modifier is not valid in components. Components are dependency injection containers designed for composition, not inheritance. The protected modifier, which enables subclass access in class hierarchies, has no meaningful application in component architecture. Components can only use public methods (for external interfaces) or private methods (for internal implementation). Use private for internal helpers.
Example (Error)
#!ek9
defines module bad.duplicate.and.modifier.component.methods
defines component
S2
//This is OK
private index1()
<- rtn <- 0
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: METHOD_MODIFIER_PROTECTED_IN_COMPONENT
protected index2()
<- rtn <- 0
Solution: Use Public or Private
#!ek9
defines component
MyComponent
process() //Public
<- result as String: helper()
private helper() //Use private
<- result as String: "Helper"
See Also
E07260: Method Modifier Protected In Closed Class
Classification: METHOD_MODIFIER_PROTECTED_IN_CLOSED_CLASS
Description
Protected access modifier is not valid in closed (final/sealed) classes. The protected modifier exists specifically to enable subclass access in inheritance hierarchies. Since closed classes cannot be extended by design, declaring protected methods is contradictory and meaningless. If a method needs restricted visibility, use private. Classes are closed by default in EK9 unless explicitly marked "as open".
Example (Error)
#!ek9
defines module bad.inherited.classes
defines class
Class17
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: METHOD_MODIFIER_PROTECTED_IN_CLOSED_CLASS
protected someMethod()
var <- 1
assert var?
Solution: Use Public or Private
#!ek9
defines class
closed MyClass
process() //Public
<- result as String: helper()
private helper() //Use private
<- result as String: "Helper"
See Also
E07270: Method Modifier Not Required In Trait
Classification: METHOD_MODIFIER_NOT_REQUIRED_IN_TRAIT
Description
Access modifiers (public, private, protected) are not supported in traits. Traits define contracts and behavior that implementing classes/types must fulfill. All trait methods are implicitly public since they form the public contract of the trait. The concepts of private or protected visibility have no meaningful application in traits, as traits cannot have implementation-only methods - they exist solely to define interfaces that other types will implement or extend.
Example (Error)
#!ek9
defines module bad.duplicate.traitmethods
defines trait
T4
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: METHOD_MODIFIER_NOT_REQUIRED_IN_TRAIT
private traitsDoNotAllowPrivateMethods()
-> arg0 as Integer
<- rtn <- 1
Solution: Remove Access Modifier
#!ek9
defines trait
Printable
print() //No access modifier needed
-> value as String
See Also
E07280: Method Access Modifier Default
Classification: METHOD_ACCESS_MODIFIER_DEFAULT
Description
The explicit 'public' access modifier is not required in EK9 - methods are public by default unless marked private or protected. While this may seem counterintuitive coming from languages like Java where 'public' is commonly written, EK9 treats explicit 'public' as redundant noise. This error specifically catches the common mistake of developers migrating from Java/C++ who habitually write 'public' on methods. Simply omit the keyword - the method is already public.
Example (Error)
#!ek9
defines module bad.classmodifier.use
defines class
C1 extends C0
@Error: SYMBOL_DEFINITION: METHOD_ACCESS_MODIFIER_DEFAULT
public someMethod()
<- rtn as String: "OK"
Solution: Specify Access Modifier
#!ek9
defines class
MyClass
public process() //Add explicit modifier
<- result as String: "Processed"
private helper() //Also explicit
<- result as String: "Helper"
See Also
E07290: Records Only Support Constructor And Operator Methods
Classification: RECORDS_ONLY_SUPPORT_CONSTRUCTOR_AND_OPERATOR_METHODS
Description
Records are restricted to constructors and operators only - regular methods are not permitted. Records are designed as immutable data carriers that hold state and implement value semantics through operators (like equality, comparison, hashing). Regular methods that would transform or compute based on record data should be implemented as external functions that take the record as a parameter, maintaining the clear separation between data (records) and behavior (functions). This enforces functional design patterns and prevents records from becoming stateful objects.
Example (Error)
#!ek9
defines module bad.duplicate.recordmethods
defines record
R2
someField <- 1
@Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: RECORDS_ONLY_SUPPORT_CONSTRUCTOR_AND_OPERATOR_METHODS
private anotherMethodName()
var <- "Steve"
assert var?
Solution: Use Operators or Functions
#!ek9
defines record
Point
x as Integer
y as Integer
operator + as pure //Operators OK
-> other as Point
<- result as Point: Point(x + other.x, y + other.y)
defines function
movePoint() //Use external function
->
point as Point
deltaX as Integer
deltaY as Integer
See Also
E07300: Declared As Null Not Needed
Classification: DECLARED_AS_NULL_NOT_NEEDED
Description
Using the nullable marker '?' on parameters, variables, or return types is redundant and not needed in EK9. The '?' suffix is typically used for optional types (like Optional<String>), but EK9's tri-state semantics (absent/unset/set) mean that all reference types already support being unset without explicit '?' notation. For parameters and variables, declaring them without '?' is sufficient - they can be unset by default. The compiler enforces this to avoid confusion between nullable declarations and Optional types.
Example (Error)
#!ek9
defines module bad.variableonly.use
defines function
test1()
@Error: SYMBOL_DEFINITION: DECLARED_AS_NULL_NOT_NEEDED
-> var as String?
<- rtn as String: String()
Solution: Use Optional or Omit
#!ek9
defines function
demo()
//Option 1: Use Optional
value as Integer?
//Option 2: Provide value
count as Integer: 0
//Option 3: Use variable with isSet semantics
name as String //Unset by default
See Also
- Variables
- Optional Types
🎉 All 215 EK9 compiler errors are now documented!
Every error includes detailed descriptions, working examples, complete solutions, and cross-references to help you quickly understand and fix compilation issues.
Need help with an error not listed here? Check the EK9 GitHub Issues or consult the relevant language documentation pages linked from the navigation menu.