Skip to content

Diagnostics

Elci reports each error with a stable code and a source location, shows the source code it is about and suggests a fix. This page explains how to read a diagnostic, how a run reports several errors, and how tools can read them. It then lists every code, and shows the most common errors with a small program that triggers each one, the exact output and the fix.

A compile error starts with one line, path:line:column: code: message. A blank line follows, then the source line with carets under what the error is about, then any hints:

src/Main.elm:6:5: ELCI2005: Unknown name 'lenght'
6| lenght [ 1, 2 ]
^^^^^^
Hint: did you mean `List.length`?
For more on ELCI2005, run `elci explain ELCI2005`.
  • The first line has the same form for every error, so editors and scripts can parse it. The path is the file of the module where the problem is, which may be a module imported by the one you compiled. A problem inside a bundled module such as List is reported where your code uses it, or otherwise as <bundled>/List.elm.
  • The code names the class of error and never changes between releases. The message says what is wrong in terms of your code: which argument, branch, list element, operator side, annotation or field has the wrong type, or which patterns a case doesn’t cover.
  • The excerpt shows the line with its number, and underlines what the error is about. For an error at the end of the file, it points just past the last code. An error in a bundled module has no excerpt.
  • Each hint suggests a likely fix: a close name, a conversion such as String.fromInt, or an import to add. An error has zero or more hints.
  • The last line names the codes of the run and the command that explains them; see elci explain. The examples in the rest of this page leave it out and show only the diagnostic. Some show only its first line, without the excerpt and hints.

Types in messages are written as in source, in backticks: Result a Int, Order. When two types of one name meet in a message, both are qualified, as in Main.Value and Julc.Ledger.Value.

For a validator, the elci.toml table follows the diagnostic, on its own line:

my-project/src/Main.elm:6:1: ELCI2013: Functions cannot be serialized as datum or redeemer
6| mint : (Int -> Int) -> ScriptContext -> Bool
^^^^
In my-project/elci.toml:6: [validators.main]

Every error makes the command exit with status 1. Codes starting with ELCI1 come from the lexer and parser, ELCI20 from type checking, ELCI21 from modules and imports, ELCI3 from code generation and ELCI4 from configuration and the backend.

The carets underline the reported token, such as a name, a qualified name or a string literal with its quotes. When the error is about a whole expression, they underline all of it. This is the case for an argument, a list element, the body of a case branch and a field access such as .price:

main : String
main =
String.fromInt (String.append "4" "2")
src/Main.elm:6:20: ELCI2002: The 1st argument to `String.fromInt` is `String`, but `String.fromInt` needs `Int`
6| String.fromInt (String.append "4" "2")
^^^^^^^^^^^^^^^^^^^^^^^

An expression over several lines is underlined on each of its lines, up to five:

main : String
main =
String.fromInt
(String.append
"4"
"2"
)
src/Main.elm:7:9: ELCI2002: The 1st argument to `String.fromInt` is `String`, but `String.fromInt` needs `Int`
7| (String.append
^^^^^^^^^^^^^^
8| "4"
^^^
9| "2"
^^^
10| )
^

The line and column of the first line are where the underline starts. A longer expression shows its first five lines, then a line with ….

Some expressions are reported at a token inside them: an operation such as "x" ++ "y" at its operator, and a call without parentheses such as String.fromInt 3 at its last argument. The caret then marks that token alone, as an underline from there would cover only part of the expression.

When the two types of a mismatch are long, It is: and It needs: lines follow the excerpt. They show the types one above the other, with the first difference marked:

type alias Order =
{ owner : String, amount : Int, deadline : Int }
amountOf : Order -> Int
amountOf order =
order.amount
main : Int
main =
amountOf { owner = "alice", amount = "10", deadline = 5 }
src/Main.elm:15:14: ELCI2002: The 1st argument to `amountOf` is `{ amount : String, deadline : Int, owner : String }`, but `amountOf` needs `{ amount : Int, deadline : Int, owner : String }`
15| amountOf { owner = "alice", amount = "10", deadline = 5 }
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
It is: { amount : String, deadline : Int, owner : String }
It needs: { amount : Int, deadline : Int, owner : String }
^^^

The message still names both types, so the first line reads the same as for short types. The lines are added when the two types together are 60 characters or longer.

On a terminal, the path and code, the carets and Hint: are in colour. Output to a file or a pipe is plain, and so is any output when the NO_COLOR environment variable is set. --color overrides this on any command:

OptionColour
--color=autoOn a terminal, unless NO_COLOR is set. The default.
--color=alwaysAlways, also in a pipe.
--color=neverNever.

The text is the same, character for character, with and without colour. The JSON report is never coloured.

A run reports every error it finds, not just the first. Compilation has three phases: lexing and parsing, name resolution, and type checking. Each phase reports all of its errors. A later phase runs only when the earlier ones found none, as in Elm, so one mistake isn’t reported again as a different error later.

fee : Int
fee =
"2"
total : Int -> Int
total amount =
amount + lenght [ 1 ]
label : Int -> String
label amount =
String.fromInt "amount"
main : Int
main =
total fee
src/Main.elm:4:1: ELCI2002: The annotation of `fee` says it is `Int`, but its definition is `String`
4| fee : Int
^^^
src/Main.elm:11:14: ELCI2005: Unknown name 'lenght'
11| amount + lenght [ 1 ]
^^^^^^
Hint: did you mean `List.length`?
src/Main.elm:16:20: ELCI2002: The 1st argument to `String.fromInt` is `String`, but `String.fromInt` needs `Int`
16| String.fromInt "amount"
^^^^^^^^
3 errors
For more on ELCI2002 and ELCI2005, run `elci explain <code>`.
  • Order. Errors are shown in source order: by module, then by line and column. A blank line separates them.
  • Count. When there is more than one error, a line such as 3 errors follows them.
  • Limit. A run shows at most 20 errors, then a line such as ... and 3 more. The count still gives the total.
  • One per declaration. Each top-level declaration reports its first error. A declaration whose definition fails still has the type its annotation gives it, so its callers are checked against the annotation and don’t report errors of their own. Above, main passes fee to total, which needs an Int. The annotation says fee is one, so main is not reported.
  • Once each. A mistake that fails several checks alike, such as each comparison inside List.sort, is reported once.
  • Phases. A module with a syntax error is not type-checked. Nor is a program with an import or qualified-name error (ELCI21xx), such as Lsit.length. Fix those, and the next run reports the type errors. An unknown unqualified name (ELCI2005), as above, is found while type checking and is reported with the type errors.
  • One at a time. Code generation (ELCI3xxx), configuration and command-line errors (ELCI4xxx) still stop the run at the first error.

A failed run’s text report ends with a line that names its codes and how to read more about them:

For more on ELCI2005, run `elci explain ELCI2005`.
For more on ELCI2002 and ELCI2005, run `elci explain <code>`.

elci explain prints the entry for a code: what it means and its common causes, a failing example with its exact output, the fix, and where to read more. ELCI2005, elci2005 and 2005 all name the same code:

$ elci explain 2005
ELCI2005: Unknown name
A value name is used that nothing in scope defines.
Common causes:
- A misspelling. The hint suggests up to three close names, local ones first.
- A name from another module that is not imported, or that its import doesn't expose. The hint
says how to reach it, such as writing `Helper.double`.
- A name a Java library module doesn't export.
A qualified name such as `List.lenght` is ELCI2104.
Example
main : Int
main =
lenght [ 1, 2 ]
`elci check` reports:
src/Main.elm:6:5: ELCI2005: Unknown name 'lenght'
6| lenght [ 1, 2 ]
^^^^^^
Hint: did you mean `List.length`?
Fix
Correct the spelling, or import the module that defines the name:
main : Int
main =
List.length [ 1, 2 ]
See also
- The Diagnostics reference, section ELCI2005.
- ELCI2104, for qualified names.

elci explain without a code lists every code with its title. The entries are part of the compiler and match its version. Each entry’s example is tested to give exactly the output it shows, and its fix to compile. An unknown code is an ELCI4001 error.

elci check, elci build, elci eval and elci test take --report=json. Each error is then printed on standard error as one JSON object on its own line (JSON Lines). A run with one error prints one line:

$ elci check --report=json
{"code":"ELCI2005","path":"src/Main.elm","line":6,"column":5,"message":"Unknown name 'lenght'","hints":["did you mean `List.length`?"],"excerpt":"6| lenght [ 1, 2 ]\n ^^^^^^","context":null}

A run with several errors prints one line for each, in the same order and up to the same limit as the text report. Read the output line by line, and parse each line as JSON:

$ elci check --report=json
{"code":"ELCI2002","path":"src/Main.elm","line":4,"column":1,"message":"The annotation of `fee` says it is `Int`, but its definition is `String`","hints":[],"excerpt":"4| fee : Int\n ^^^","context":null}
{"code":"ELCI2005","path":"src/Main.elm","line":11,"column":14,"message":"Unknown name 'lenght'","hints":["did you mean `List.length`?"],"excerpt":"11| amount + lenght [ 1 ]\n ^^^^^^","context":null}
{"code":"ELCI2002","path":"src/Main.elm","line":16,"column":20,"message":"The 1st argument to `String.fromInt` is `String`, but `String.fromInt` needs `Int`","hints":[],"excerpt":"16| String.fromInt \"amount\"\n ^^^^^^^^","context":null}

The JSON report has no count line, no ... and N more line and no elci explain line.

FieldTypeContents
codestring or nullThe ELCI code; null for a failure that has no code
pathstring or nullThe file, as on the first line of the text report
line, columnnumber or nullThe position, counting from 1
messagestringThe message, without the path and code
hintsarray of stringsThe hints, without Hint:; empty when there are none
excerptstring or nullThe source lines and their caret lines, separated by \n; null when there is no source to show
contextstring or nullFor a failure in a named validator, its elci.toml line and table; otherwise null

The It is: and It needs: lines of a long mismatch are not part of the JSON; the message names both types. A failure in a validator names its table in context:

{"code":"ELCI2013","path":"my-project/src/Main.elm","line":6,"column":1,"message":"Functions cannot be serialized as datum or redeemer","hints":[],"excerpt":"6| mint : (Int -> Int) -> ScriptContext -> Bool\n ^^^^","context":"my-project/elci.toml:6: [validators.main]"}

A failure without a source location keeps the same fields, with null location fields:

{"code":"ELCI4001","path":null,"line":null,"column":null,"message":"unknown validator 'treasury'; configured: main","hints":[],"excerpt":null,"context":null}

The exit status is the same with or without the option. Results stay text on standard output: the lines of elci check and elci build, the value and budget of elci eval or its evaluation failed: line, and test results, including failing tests. The other commands reject the option with ELCI4001.

CodeMeaning
ELCI1001Invalid source character or literal
ELCI1002Malformed or unsupported syntax
ELCI2001Invalid pattern or expression form
ELCI2002Type mismatch, or a type that would have to contain itself
ELCI2003Unsupported equality, ordering or append type; a constrained type variable that is missing or misnamed; a Dict decoded from Data whose keys aren’t comparable
ELCI2004Missing required top-level signature
ELCI2005Unknown value name
ELCI2006Duplicate binder or unreachable pattern
ELCI2007Non-exhaustive case expression
ELCI2008Eager recursive value cycle
ELCI2009Non-concrete handler, entrypoint or property
ELCI2010Unknown or unconstrained record field
ELCI2011Duplicate type or constructor, or a built-in constructor or type name (Just, Nothing, True, False; Int, Maybe, List and the other built-in types)
ELCI2012Invalid type, alias, arity or alias cycle; Tuple or Unit written as a type name
ELCI2013Unsupported serialized boundary type, such as a Value in a datum, or a value without a Data encoding (a function or an Array) stored in data
ELCI2014Validator handler signature does not match its purpose, parameters or datum profile
ELCI2101Unknown or missing module or imported export; a source module importing the test library
ELCI2102Ambiguous name: two imports expose different things under it
ELCI2103Entrypoint or handler is not exposed
ELCI2104Module path, ownership or cycle error; a reserved module name or alias; a name a module lacks, an Elm function Elci doesn’t have, or a module used without its import
ELCI2105Unsupported Java export signature, with the provider’s reason; a Java export whose result can hold a Value
ELCI3001The inferred program cannot be lowered, such as Julc.Data.decode or a Java library generic at a type nothing determines
ELCI3002Specialization limit or polymorphic recursion
ELCI3003A generic library type argument or a Data conversion needs a Data encoding; a Java library generic used at a type containing a Dict or a Value
ELCI4001Invalid project configuration or command-line request, including a [java-modules] key that names a core module and an elci apply parameter that doesn’t fit its type
ELCI4002Julc’s backend rejected the generated code
ELCI4003The Julc backend lacks a capability the program needs
main : Int
main =
1.5
src/Main.elm:6:6: ELCI1001: Float is unsupported; represent fractional amounts using an explicit integer scale
6| 1.5
^

Fix: use integers with an explicit scale, such as lovelace instead of ada or basis points instead of percentages. The same code reports tabs, unsupported characters such as % or ' and unknown string escapes.

The message says what is wrong. Refutable patterns in function arguments are a common case:

first : List Int -> Int
first (x :: _) =
x
src/Main.elm:5:8: ELCI1002: Function parameters require irrefutable patterns; use case for refutable matching
5| first (x :: _) =
^

Fix: match with case, and handle every shape:

first : List Int -> Int
first values =
case values of
x :: _ ->
x
[] ->
0

Other ELCI1002 messages include Comparison operators cannot be chained; use && or explicit parentheses (for 1 < 2 < 3), Mixing (<|) and (|>) needs parentheses: they associate in opposite directions, as in Elm (for f <| x |> g), Extensible records are unsupported; use a closed record and Tuples have two or three elements.

main : Int
main =
7 / 2
src/Main.elm:6:7: ELCI2001: Float division is unsupported; use // for integer quotient
6| 7 / 2
^

Fix: 7 // 2, together with modBy or remainderBy when you need the rest. The function (/) is rejected the same way, with a suggestion to use (//).

ELCI2001 also covers patterns that can’t match their value’s shape. A constructor pattern must give the constructor all of its arguments:

main : Int
main =
case Just 1 of
Just x y ->
x
Nothing ->
0
src/Main.elm:7:9: ELCI2001: The constructor Just takes 1 argument, but this pattern gives it 2
7| Just x y ->
^^^^

A misspelt constructor in a pattern gets a suggestion:

src/Main.elm:7:9: ELCI2001: Unknown constructor Jsut in this pattern
7| Jsut n ->
^^^^
Hint: did you mean `Just`?

A record pattern such as { index } can’t take apart a ledger record. Read its fields instead:

src/Main.elm:10:9: ELCI2001: TxOutRef is a library record, which a pattern cannot take apart; read its fields with `.index` instead
main : String
main =
String.fromInt "42"
src/Main.elm:6:20: ELCI2002: The 1st argument to `String.fromInt` is `String`, but `String.fromInt` needs `Int`
6| String.fromInt "42"
^^^^

The message names the part of the code whose type differs, and the type it needs:

WhereExample message
An argumentThe 1st argument to `String.fromInt` is `String`, but `String.fromInt` needs `Int`
A value piped in with |>The 1st argument to `double`, the value piped in, is `String`, but `double` needs `Int`
Too many arguments`max` takes 2 arguments, but it is given 3
A value applied like a function`fee` is `Int`, not a function, so it cannot take an argument
The condition of an ifThe condition of this if is `Int`, but it must be `Bool`
The branches of an ifThe branches of this if have different types: the then branch is `Int`, but the else branch is `String`
A branch of a caseThe 2nd branch of this case is `String`, but the first branch is `Int`
A patternThis pattern matches `Maybe a`, but the value it is matched against is `Int`
A list elementThe 3rd element of this list is `String`, but the elements before it are `Int`
An operatorThe left side of :: is `String`, but the list on the right holds `Int`, The two sides of ++ have different types: the left is `String`, but the right is `Int`
A compositionThe left side of >> returns `String`, but the right side takes `Int`
An annotationThe annotation of `add` says it returns `Int`, but its body is `String`, The annotation of `add` gives it 1 argument, but its definition has 2 parameters
A record fieldThe field `owner` is `String`, but it is used here as `Int`

A too-many-arguments error also hints that a missing comma or parentheses can make values look like extra arguments.

A signature that claims more than the body delivers is also a mismatch:

identity : a -> a
identity value =
value + 1
src/Main.elm:4:1: ELCI2002: The annotation of `identity` says `a` can be any type, but its definition needs `Int`
4| identity : a -> a
^^^^^^^^
Hint: write `Int` in the annotation, or make the definition work for any type

Fix: make the signature match the body (increment : Int -> Int), or the body match the signature. An annotation with two type variables that the definition needs to be the same says The annotation of `pick` says `a` and `b` can be different types, but its definition needs them to be the same. The annotations of mutually recursive functions are all read before any body is checked, so a mistake is reported in the body that makes it.

Arithmetic operators name the types they accept, and suggest the right operator where there is one:

src/Main.elm:6:7: ELCI2002: The operator + works on Int values, not String; use ++ to append

Comparisons on a type that has no order are ELCI2003, below.

Hints. A mismatch between types that are often confused carries a hint with the conversion:

main : Int
main =
List.head [ 1, 2 ] + 1
src/Main.elm:6:24: ELCI2002: The operator + works on Int values, not Maybe Int
6| List.head [ 1, 2 ] + 1
^
Hint: this is a Maybe: handle `Nothing` with a case, or use `Maybe.withDefault`

The hints cover Int and String (String.fromInt), String and Bytes (Bytes.fromString, Bytes.toString), Bytes and a ledger hash such as PubKeyHash (the constructor, or the .hash field), a Maybe or Result and a plain value (a case, Maybe.withDefault, Result.withDefault or Just), Value and Int (Value.lovelaceOf, Value.lovelace), Data and a typed value (Data.decode, Data.encode), and a function where a value is needed (a missing argument). An error shows at most two hints, and only those that fit the types. A pattern of the wrong type gets no hint.

A type that would have to contain itself, as when a generic function calls itself with a tuple of its argument, is reported as ELCI2002: This needs a type that contains itself: `a` would have to be `( a, a )`.

ELCI2003: unsupported equality, ordering or append

Section titled “ELCI2003: unsupported equality, ordering or append”
double : Int -> Int
double x =
x * 2
main : Bool
main =
double == double
src/Main.elm:11:12: ELCI2003: The operator == cannot compare Int -> Int values: functions have no equality
11| double == double
^^

Equality is rejected for functions, Map, Array and imported types without approved equality, such as TxOut. A Dict has equality when its values do, and a Value and Data always have it. When the offending type is inside another, the message names it: The operator == cannot compare List (Int -> Int) values: Int -> Int is part of them, and functions have no equality. ++ needs a String or a List: 1 ++ 2 gives ELCI2003: The operator ++ appends String or List values, not Int.

Fix: compare the values you care about, such as a map’s lookups or a decoded datum, or keep keyed data in a Dict, which has equality.

Ordering (<, compare, max, List.sort and the rest) needs a comparable type. The message names the operation and the part of the type that isn’t comparable:

main : Bool
main =
( 1, True ) < ( 2, False )
src/Main.elm:6:17: ELCI2003: The operator < needs comparable values, but Bool, in ( Int, Bool ), is not comparable. Comparable types are Int, String, Bytes, byte-string ledger types such as PubKeyHash, and lists and tuples of comparable types
6| ( 1, True ) < ( 2, False )
^

For records and custom types, the message goes on to suggest a key:

src/Main.elm:10:5: ELCI2003: `List.sort` needs comparable values, but { age : Int, name : String } is not comparable. Comparable types are Int, String, Bytes, byte-string ledger types such as PubKeyHash, and lists and tuples of comparable types. Compare a comparable field instead, or sort with List.sortBy or List.sortWith

Fix: compare a comparable field, or sort with List.sortBy (\person -> person.age) or List.sortWith.

A signature whose type variable is compared or appended must say so with its name, as in Elm (constrained type variables):

larger : a -> a -> a
larger x y =
if x > y then
x
else
y
src/Main.elm:6:10: ELCI2003: The operator > compares values of type `a`, but the annotation of `larger` says `a` can be any type. Rename it `comparable` (as in Elm) to require comparable values
6| if x > y then
^

Fix: write larger : comparable -> comparable -> comparable. For ++, the message suggests appendable. A caller that then passes the wrong type is reported at the call: ELCI2003: `twice` appends String or List values, not Int. Elm’s number has its own message:

src/Main.elm:4:1: ELCI2003: The annotation of `total` uses `number`, Elm's type variable for Int or Float. Elci has one number type: write Int

Decoding a Dict checks that its keys are in order, so a Dict in a datum, a redeemer, a parameter or a Julc.Data.decode result needs comparable keys:

src/Main.elm:9:5: ELCI2003: A Dict decoded from Data needs comparable keys, to check that they are in order; Bool is not comparable. Use a Map instead

Fix: use a Map, whose keys may be any type with a Data encoding.

double x =
x * 2
src/Main.elm:4:1: ELCI2004: Top-level declarations require a type signature
4| double x =
^^^^^^

Fix: add double : Int -> Int directly above the definition. Local definitions do not need one.

main : Int
main =
lenght [ 1, 2 ]
src/Main.elm:6:5: ELCI2005: Unknown name 'lenght'
6| lenght [ 1, 2 ]
^^^^^^
Hint: did you mean `List.length`?

Fix: correct the spelling, or import the module that defines the name. The hint lists up to three close names. It looks first at the names the module can write unqualified, local ones included, and then at qualified names. A name is close when it differs only in case, by one edit for a name of three to five characters, or by two edits for a longer name. Swapping two neighbouring letters counts as one edit.

A name that an imported module exposes, but that its import doesn’t list, gets a hint that says how to reach it. Here the module Helper exposes double:

src/Main.elm
module Main exposing (main)
import Helper
main : Int
main =
double 4
src/Main.elm:8:5: ELCI2005: Unknown name 'double'
8| double 4
^^^^^^
Hint: write `Helper.double`, or expose `double` in its import

A name that a Java library module doesn’t export is reported with a reminder to check its exports:

src/Main.elm:8:5: ELCI2005: Unknown name 'Math.maximum'; check that the imported module exports it

A qualified name whose module is missing, not imported or lacks the name is ELCI2104; see module and import errors.

ELCI2006: duplicate binder or unreachable pattern

Section titled “ELCI2006: duplicate binder or unreachable pattern”
describe : Maybe Int -> Int
describe value =
case value of
Just n ->
n
Nothing ->
0
Just _ ->
1
src/Main.elm:13:9: ELCI2006: Unreachable pattern
13| Just _ ->
^^^^

Fix: remove the branch that can never match. A pattern that binds the same name twice, such as ( x, x ), gives ELCI2006: Duplicate pattern variable x.

type Action
= Buy Int
| Sell Int
| Cancel
amount : Action -> Int
amount action =
case action of
Buy value ->
value
Sell value ->
value
src/Main.elm:12:5: ELCI2007: This case does not cover every possibility; it is missing `Cancel`
12| case action of
^^^^
Hint: add a branch for each missing pattern

Fix: add a Cancel -> branch. Prefer naming every constructor over _ ->, so that adding a constructor later points you to every case to update.

The message lists the missing patterns as you would write them, including nested ones:

describe : Maybe (Result String Int) -> String
describe value =
case value of
Just (Ok n) ->
String.fromInt n
src/Main.elm:6:5: ELCI2007: This case does not cover every possibility; it is missing `Nothing` and `Just (Err _)`
6| case value of
^^^^
Hint: add a branch for each missing pattern

The list is complete: a branch for each pattern in it makes the case exhaustive. It shows up to three patterns, then how many more there are, as in it is missing `Square _`, `Triangle _ _ _`, `Line` and 1 more. Lists and literals are covered too: a case on a list with branches for [] and [ _ ] is missing _ :: _ :: _, and one on an Int or a String without a _ branch is missing _.

main : Int
main =
let
a =
b + 1
b =
a + 1
in
a
src/Main.elm:7:9: ELCI2008: Cyclic eager values are unsupported; recursion requires functions
7| a =
^

Fix: values are computed eagerly, so they cannot depend on each other in a loop. Break the cycle, or turn the recursion into a function.

main : List a
main =
[]
src/Main.elm:4:1: ELCI2009: Entrypoints require concrete types
4| main : List a
^^^^

A validator handler gets Handler Main.mint requires a concrete type, and a property test with generic arguments gets a similar message. Fix: replace the type variable with a concrete type, such as List Int.

main : Int
main =
let
order =
{ amount = 5 }
in
order.price
src/Main.elm:10:10: ELCI2010: Missing field 'price'; available fields: [amount]
10| order.price
^^^^^^

Fix: use one of the listed fields. A field name close to one of them, such as order.amont, also gets the hint did you mean `amount`?. When Elci cannot tell which record a field access belongs to, add a signature with the record type.

ELCI2011 and ELCI2012: invalid type declarations

Section titled “ELCI2011 and ELCI2012: invalid type declarations”

Two custom types in one module cannot share a constructor name. No module may declare the built-in constructors Just, Nothing, True or False, or a type named Int, Bool, String, Bytes, Data, List, Maybe, Map, Array or Float:

src/Main.elm:9:6: ELCI2011: Duplicate constructor Buy: the type Action has one too
src/Main.elm:4:6: ELCI2011: Nothing is a constructor of the built-in Maybe type; choose another name
src/Main.elm:4:6: ELCI2011: Bytes is a built-in type; choose another name

A type applied to the wrong number of arguments, and type aliases that refer to each other in a loop, are ELCI2012:

src/Main.elm:4:1: ELCI2012: The type List takes 1 argument, but it is given 2
src/Main.elm:4:12: ELCI2012: Cyclic type alias: A -> B -> A

Tuple and unit types have their own syntax. Tuple Int Int and Unit are ELCI2012:

src/Main.elm:4:8: ELCI2012: Write tuple types as ( a, b ) or ( a, b, c )
src/Main.elm:4:10: ELCI2012: Write the unit type as ()

An unknown type name in a signature is also ELCI2012, reported at the declaration. A close type name is suggested:

main : Strin
main =
"x"
src/Main.elm:4:1: ELCI2012: Type is not declared or imported in this module: Strin
4| main : Strin
^^^^
Hint: did you mean `String`?

Fix: declare or import the type, or correct its spelling.

A datum, redeemer or parameter must have a strict Data encoding:

mint : (Int -> Int) -> ScriptContext -> Bool
mint _ _ =
True
my-project/src/Main.elm:6:1: ELCI2013: Functions cannot be serialized as datum or redeemer
6| mint : (Int -> Int) -> ScriptContext -> Bool
^^^^
In my-project/elci.toml:6: [validators.main]

A Value is rejected the same way:

my-project/src/Main.elm:7:1: ELCI2013: A Value cannot be part of a datum, redeemer or parameter yet: the ledger's strict boundary cannot check its form. Store a List ( PolicyId, TokenName, Int ) and rebuild it with Value.fromList
7| mint : Value -> ScriptContext -> Bool
^^^^
In my-project/elci.toml:6: [validators.main]

So is a ledger type that contains one, such as TxOut: ELCI2013: TxOut has no approved strict Data boundary (BOUNDARY); for example records containing a ledger Value (such as TxOut) cannot be checked.

Fix: use your own record or custom type, or a ledger type with an approved boundary such as TxOutRef or PubKeyHash. For an amount of tokens, store a List ( PolicyId, TokenName, Int ) and rebuild the value with Value.fromList.

The same code rejects an Array stored anywhere in data (a record, list or tuple field, or a datum, redeemer or parameter), in any project:

module Main exposing (main)
import Array exposing (Array)
type alias Book =
{ pages : Array Int }
main : Int
main =
let
books =
[ { pages = Array.fromList [ 1 ] } ]
in
List.length books
src/Main.elm:10:1: ELCI2013: Arrays have no Data encoding and cannot be stored in data; keep a List and convert it with Array.fromList
10| main : Int
^^^^

ELCI2014: handler signature does not match

Section titled “ELCI2014: handler signature does not match”

A spend handler with only a redeemer and context:

spend : () -> ScriptContext -> Bool
spend _ _ =
True
my-project/src/Main.elm:6:1: ELCI2014: Handler Main.spend does not have the spend signature datum -> redeemer -> ScriptContext -> Bool that [validators.main] needs (0 parameters); its type is () -> ScriptContext -> Bool; it has `Bool` where the configuration needs `ScriptContext -> Bool`
6| spend : () -> ScriptContext -> Bool
^^^^^
In my-project/elci.toml:6: [validators.main]

The message gives the signature the table needs, how many parameters it declares and the handler’s actual type. The part in parentheses names the first pair of types that differ. The same code reports a missing parameter argument (does not have the mint signature owner -> redeemer -> ScriptContext -> Bool that [validators.main] needs (1 parameter)) and a datum that should be a Maybe (uses spend-datum = "optional", so its datum must have type Maybe d, not Int). Fix: make the signature match the table in elci.toml: parameters first, then the datum (for spend), the redeemer and the ScriptContext.

my-project/src/Main.elm:11:1: ELCI2103: Handler Main.mint must be exposed by its module
11| mint : () -> ScriptContext -> Bool
^^^^
In my-project/elci.toml:6: [validators.main]

An [entrypoint] function that its module doesn’t expose gets ELCI2103: Entrypoint Main.main must be exposed by its module.

Fix: add the handler, or the [entrypoint] function, to the module’s exposing list.

A module that is neither a source file nor a [java-modules] mapping, or an unknown ledger type, is ELCI2101, reported at the import:

import Julc.Nope as Nope
src/Main.elm:3:1: ELCI2101: Missing source module: Julc.Nope; add Julc/Nope.elm under a source directory, or map a Java library to it under [java-modules]
3| import Julc.Nope as Nope
^^^^^^
import Julc.Ledger exposing (Hash)
src/Main.elm:3:1: ELCI2101: Unknown ledger record type Hash
3| import Julc.Ledger exposing (Hash)
^^^^^^

A misspelt module, ledger type or exposed name in an import gets a suggestion:

src/Main.elm:3:1: ELCI2101: Unknown ledger type Credentail
3| import Julc.Ledger exposing (Credentail(..))
^^^^^^
Hint: did you mean `Credential`?

The test library (Expect, Scenario, Property) is only for test modules. A source module that imports it is rejected. elci test reports:

src/Main.elm:3:1: ELCI2101: Only test modules can import Expect; it is part of the test library, not available to elci build

elci check and elci build report it when a validator reaches that module:

src/Main.elm:3:1: ELCI2101: Expect is part of the test library; only test modules can import it

Imports of existing modules fail with ELCI2104 when the name is not exposed:

import Map exposing (lookup)
src/Main.elm:3:1: ELCI2104: Map does not expose 'lookup'
3| import Map exposing (lookup)
^^^^^^

A qualified name gives one of three messages, depending on the module. The hint suggests close names:

SituationMessage
The module is imported, but doesn’t expose the nameModule Helper does not expose 'doubel'
A project or Java module exists, but this module doesn’t import itModule Helper is not imported; add `import Helper`
No imported module has that nameNo module Lsit is imported, and none has that name
main : Int
main =
Lsit.length [ 1, 2 ]
src/Main.elm:6:5: ELCI2104: No module Lsit is imported, and none has that name
6| Lsit.length [ 1, 2 ]
^^^^^^^^^^^
Hint: did you mean `List.length`?

A qualified name must use the name the module is imported under. After import Shop.Pricing without as Pricing, Pricing.priceOf gets the third message; write Shop.Pricing.priceOf.

A qualified name that a core module lacks lists what the module has. For Elm functions that Elci doesn’t provide, the message says what to use instead:

src/Main.elm:8:5: ELCI2104: Elci's Map module has no lookup. It has empty, filter, foldl, foldr, fromList, get, insert, isEmpty, keys, map, member, remove, singleton, size, toList, update, values
src/Main.elm:6:5: ELCI2104: Elci has no Debug.log; use Debug.trace message value, which returns value
src/Main.elm:6:5: ELCI2104: Elci has no String.length; a String is UTF-8 text; Bytes.length (Bytes.fromString s) is its byte length
src/Main.elm:6:5: ELCI2104: Elci has no Bytes.lessThan; bytes are comparable: write a < b

Using a core module that isn’t imported by default, such as Bytes, Dict, Map, Array or Julc.Data, without its import names the import to add:

src/Main.elm:6:5: ELCI2104: Bytes.fromString needs the Bytes module; add `import Bytes`

A module imported with an alias is used through the alias:

src/Main.elm:8:5: ELCI2104: Map is imported as M; write M.get
src/Main.elm:8:5: ELCI2104: Shop.Pricing is imported as P; write P.priceOf

ELCI2104 also covers exposing (..) on a Java module, a file whose module line does not match its path, and import cycles:

src/Main.elm:3:1: ELCI2104: Java modules cannot be imported with exposing (..); list the exported names you use
src/Pricing.elm:1:1: ELCI2104: Module header Finance.Pricing does not match its source path, which names module Pricing
src/Pricing.elm:3:1: ELCI2104: Import cycle: Main -> Pricing -> Main

Core module names are reserved, for modules and for aliases (see reserved names):

src/String.elm:1:1: ELCI2104: The module name String is taken by Elci's core library; rename this module
src/Tx.elm:1:1: ELCI2104: The module name Tx is taken by Elci's core library; rename this module
src/Set.elm:1:1: ELCI2104: The module name Set is reserved for Elci's core library; rename this module
src/Main.elm:3:1: ELCI2104: The alias String names a core module that every module imports; choose another alias for Utils

Modules whose names start with Elci. belong to the core library too, and the ones it uses internally can’t be imported: ELCI2104: Elci.Assoc is internal to Elci's core library; use Map or Dict, and ELCI2104: Elci.Value is internal to Elci's core library; use Value. A Value has no constructors to expose: import Julc.Ledger exposing (Value(..)) gives ELCI2104: Value has no approved constructors or patterns; its values come from the Value module.

A name that two imports expose, one of them possibly a default import, is ELCI2102 where you use it:

src/Main.elm:8:5: ELCI2102: max is ambiguous: it could be Basics.max or Utils.max. Write the one you mean, like Basics.max, or stop exposing the other

An ambiguous type name is reported at the declaration whose annotation uses it:

src/Main.elm:6:1: ELCI2102: The type Order is ambiguous: it could be Basics.Order or Shop.Order. Write the one you mean, like Basics.Order, or stop exposing the other

Fix: import the module, correct the name (Map.get, not lookup), rename your module or alias, or qualify one of the ambiguous names. Listing the name explicitly, as in import Utils exposing (max), also resolves a clash with a default import.

main : Int
main =
case Math.divMod 7 2 of
_ ->
0
src/Main.elm:8:10: ELCI2105: org.julclang.stdlib.lib.MathLib.divMod: Unsupported on-chain source type: Tuple2<BigInteger, BigInteger>
8| case Math.divMod 7 2 of
^^^^^^^^^^^

Fix: use another export, such as Math.floorDiv and Math.floorMod, or Elci operators.

An export whose result can hold a ledger Value, directly or in a field of a record or union, is rejected too. Java libraries such as ValuesLib build values in their own order, and Elci relies on every value being canonical. Julc’s ContextsLib and OutputLib are exempt, because they only pass along the ledger’s own values:

src/Main.elm:9:5: ELCI2105: org.julclang.stdlib.lib.ValuesLib.add: returns a Value, which Java libraries do not keep in the ledger's canonical order; use the core Value module

Fix: use the core Value module, here Value.add. See Java libraries for every reason an export can be rejected.

ELCI3001: type is not known where it is used

Section titled “ELCI3001: type is not known where it is used”

A type that nothing in the program determines, such as the element type of [], is fine for most code: List.length [] is 0 and Julc.Data.encode [] is an empty list, as in Elm. Operations that create a value of the type need to know it. These are Julc.Data.decode and generic Java library functions:

import Julc.Data as Data
main : Int
main =
List.length (Data.decode (Data.encode [ 1, 2 ]))
src/Main.elm:8:18: ELCI3001: Cannot determine the type converted by Julc.Data.decode; add a type annotation
8| List.length (Data.decode (Data.encode [ 1, 2 ]))
^^^^^^^^^^^

List.length works on any list, so nothing tells Elci what the decoded list contains, and it cannot pick a decoder. Fix: give the value a type:

main : Int
main =
let
amounts : List Int
amounts =
Data.decode (Data.encode [ 1, 2 ])
in
List.length amounts

A generic Java library function needs its type arguments. When a core module such as Map uses one at a type nothing in your program determines, the error is reported at your call:

src/Main.elm:8:5: ELCI3001: `Map.size` is used here at a type that is not known (Map a b -> Int); add a type annotation, for example to a let binding

That one comes from Map.size Map.empty; annotate the map (noPrices : Map String Int).

A program may create at most 256 specializations of its generic functions. This happens with polymorphic recursion that inference did not already reject. Fix: make the recursive function monomorphic.

Generic Java methods and Julc.Data conversions need a type with a Data encoding:

src/Main.elm:8:5: ELCI3003: Type argument T = Unit of firstOr must have a Plutus Data encoding (DATA_ENCODABLE); functions, unit and native values do not
src/Main.elm:12:13: ELCI3003: No strict decoder for org.julclang.ledger.TxOut: The library provider cannot supply decode_strict record TxOut: [JULC0051] Library request decode_strict record TxOut is not supported: org.julclang.ledger.TxOut does not approve DECODE_STRICT

Encoding a function names the reason:

import Julc.Data as Data
double : Int -> Int
double x =
x * 2
main : Int
main =
List.length [ Data.encode double ]
src/Main.elm:13:19: ELCI3003: No encoder for Int -> Int: functions have no Data encoding
13| List.length [ Data.encode double ]
^^^^^^^^^^^

Fix: use a Data-encodable type. Functions, () and Array have no encoding, and TxOut can’t be decoded. A Value converts on its own, but a type that contains one can’t be decoded yet, and a list or tuple of values can’t be encoded: convert them with Value.toList and back with Value.fromList.

A Java library’s generic function can’t be used at a type that contains a Dict. The library could build the dictionary itself and skip the check that its keys are in order:

src/Main.elm:14:18: ELCI3003: Type argument a = Dict Int Int of filter contains a Dict, which a Java library cannot keep sorted; pass a Map or a List and build the Dict with Dict.fromList

Fix: pass the library a Map or a List, and build the Dict with Dict.fromList. A type that holds a Value is rejected the same way:

src/Main.elm:9:5: ELCI3003: Type argument a = Value of filter contains a Value, which a Java library does not keep in the ledger's canonical order; build it with the Value module

Use the core List and Value modules instead.

elci: ELCI4001: unknown validator 'treasury'; configured: main
elci: ELCI4001: eval runs an [entrypoint] function; test validators with elci test
elci: ELCI4001: parameter owner is not a well-formed org.julclang.ledger.PubKeyHash value
elci: ELCI4001: parameter caps has a Dict whose keys are not strictly ascending; write its keys sorted and without repeats
elci: ELCI4001: src/Main.elm is not valid UTF-8 text; save it as UTF-8

The message says what to change. A source file must be UTF-8 text. A Dict parameter must list its keys in ascending order, each once; see parameterized validators.

Mistakes in elci.toml, in the arguments of elci init and on the command line have this code too:

elci: ELCI4001: Unknown configuration key: [project] name
elci: ELCI4001: Unsupported purpose: lend; expected spend, mint, withdraw, certify, vote, propose or function
elci: ELCI4001: Unknown option: --frob
elci: ELCI4001: the [entrypoint] function main is not defined in module Main
elci: ELCI4001: my-project/elci.toml not found; a project needs an elci.toml, which elci init creates
elci: ELCI4001: Source directory not found: my-project/lib
elci: ELCI4001: library-jars file my-project/libs/pricing.jar not found
elci: ELCI4001: No source file for module Nope; add Nope.elm under a source directory

A problem with one validator’s table names the table after the code:

elci: ELCI4001: my-project/elci.toml:6: [validators.main]: Handler Main.spent for spend: module Main does not declare spent
elci: ELCI4001: my-project/elci.toml:6: [validators.main]: Handler Vault.spend for spend: no source module Vault
elci: ELCI4001: my-project/elci.toml:6: [validators.main]: [java-modules] may not map Value, a module name of Elci's core library

The CLI and elci.toml references list the others. A few errors have no code yet; they are listed below.

ELCI4002 means Julc’s PIR verifier rejected the code Elci generated. It is reported at the Elci declaration involved and keeps Julc’s own JULC00xx code in the message. ELCI4003 means the Julc backend in use lacks a capability the program needs, which happens only with a mismatched Julc build. Both point at a compiler problem rather than a mistake in your code: please report them with the program that triggers them.

Three errors from reading a Java library are still reported without a code or location. Each is printed after elci:, and in the JSON report its code is null:

MessageCause
On-chain Java source not found: org.julclang.stdlib.lib.NopeLibAn imported [java-modules] class that does not exist.
Cannot resolve type: ?. Supported types: ...A Java library that uses a wildcard type such as JulcList<?>.
Unknown type: T. Did you mean: Vote?A Java library that declares a parameterized record such as Box<T>.