Skip to main content

Diagnostic codes

Every compiler diagnostic carries a stable code:

error<E400>: Type mismatch: expected 'Int', found 'List<Int>'
--> src/main.vr:5:18
│
5 │ let y: Int = xs;
│ ^^

This page covers the codes you are most likely to meet, not all of them — the compiler's own registry holds considerably more, and verum explain <code> prints the description for any code, listed here or not.

Codes are grouped by category, and the number range tells you which phase rejected the program. That is often enough to know where to look: an E1xx means the compiler could not find a name, so the problem is a mount or a spelling; an E4xx means it found everything and the types disagree.

RangeCategoryPhase
E0xxParseLexing and parsing
E1xxName resolutionBinding names to declarations
E2xxModulemount resolution and visibility
E3xxMemoryCBGR: moves, borrows, lifetimes
E4xxTypeInference, unification, protocols
E5xxVerificationContracts, refinements, SMT
E6xxContextThe using [...] dependency system
E7xxAsyncFutures, tasks, cancellation
E8xxFFIForeign boundary and ABI
E9xxInternalCompiler bugs — please report

Four-digit codes are a different scheme​

A code with FOUR digits after the E does not follow the table above, and reading it as if it did sends you to the wrong phase: E0101 is a memory error, not a parse error.

error<E0101>: use-after-free detected ← memory, not E0xx parse
error<E0900>: denied lint: unstable intrinsic ← lint, a category the
three-digit table has no
range for at all
RangeCategoryCodesExample
E00xxParse / Internal11E0001 expected function body or semicolon
E01xxMemory3E0101 use-after-free detected
E02xxType5E0203 ? operand error type does not convert
E03xxType / Context / Verification19E0307 advanced protocol constraint unsatisfied
E04xxModule / Name / Parse / Type9E0401 stdlib bootstrap could not resolve a module surface
E05xxLint / Verification2E0500 denied lint (general)
E06xxType2E0601 non-exhaustive patterns
E07xxContext / Internal3E0700 VBC codegen received the wrong input form
E08xxContext / Internal4E0800 VBC monomorphization received the wrong input form
E09xxLint7E0900 denied lint: unstable intrinsic
E10xxLint6E1000 denied lint: unused stage

The parse family is hexadecimal​

Forty-eight codes carry a LETTER where you expect a digit — E0A0 through E0E2. They are not typos. The parse family is numbered in HEXADECIMAL, and has been all along: E010 is parse error 16, not 10, and E0E2 is parse error 226. Below E0A0 every code is spelled with digits alone, so nothing on the page gives the base away — which is why the family reads as decimal right up until it does not.

Every one of the 48 is a parse error, so the E0xx row of the first table is still the right place to look.

error<E0E2>: Parse error: 'assert!' is Rust macro syntax, not valid in Verum
help: did you mean `assert(...) (without !)`?
RangeCountFirstLast
E0Axx13E0A0 throw without expressionE0AC invalid yield
E0Bxx16E0B0 generic type args unclosed angleE0BF invalid unary op
E0Cxx10E0C0 tagged literal missing stringE0C9 invalid let pattern
E0Dxx6E0D0 trailing separatorE0D5 empty shape params
E0Exx3E0E0 rust keyword usedE0E2 rust macro syntax

The E0Exx range is the Rust-migration range, and it is exactly three codes wide — a Rust keyword (E0E0), a Rust type name (E0E1), a name!(...) macro call (E0E2). They are the only three codes in the whole registry whose description mentions Rust at all.

The registry contains 361 codes in all — 202 three-digit, 111 four-digit, 48 hexadecimal. The sections below cover the three-digit families, which are the ones ordinary programs hit; for any code in any of the three schemes, verum explain <code> prints its entry, and for E0E2 it prints the whole Rust-to-Verum mapping table.

Parse — E0xx​

CodeMeaningEmitted?
E001unterminated character literalyes
E002invalid escape sequenceyes
E003invalid number literalyes
E004empty character literalyes
E005invalid interpolation syntaxyes
E006unknown token/characteryes
E007invalid float literalno

E007 is a registry entry with no emit site: a malformed float is reported by the integer-literal code or by E001 on the token that follows it. Cited here because the code exists and a reader searching for it should learn that, not wonder why it never appears.

A parse error worth calling out separately, because it reads oddly the first time: putting a record literal directly in a match scrutinee makes the parser take the literal's { as the match body.

match Shape.Rect { w: 3, h: 4 } { ... } // E087 / E018
let s = Shape.Rect { w: 3, h: 4 }; // bind first
match s { ... }

Name resolution — E1xx​

CodeMeaningEmitted?
E100undefined variableyes
E101type not found — no declaration of that name is in scopeyes
E102wrong number of arguments to a function or methodyes
E103field not found on typeyes
E104duplicate definitionno
E105ambiguous name — it could refer to several protocols, or be imported from more than one moduleyes
E106unresolved type placeholderyes

E104 has no emit site — a redeclaration is reported by the type codes. E106 was missing from this table and does fire: it is the one you get when a type is referenced but never defined, as distinct from E101 (undefined type at a use site).

E102 also covers calling a function with too few arguments — the message names the arity:

error<E102>: Function requires at least 5 arguments, got 4 (calling `verify_pss_prehashed`)

E103 is the one to read carefully when a field name looks right. It means the type has no such field — usually a rename the call site never followed (pre_release vs prerelease, peer_addr vs peer).

Module — E2xx​

CodeMeaningEmitted?
E200import not foundno — see below
E201circular importyes
E202visibility error — the named item is not visible from this moduleyes
E203module not foundno — see below
E204circular constant dependencyyes
Three of these four never fire

only E201 has an emit site. E200, E202 and E203 are registry entries that nothing produces, and the conditions they name are reported by the type codes instead:

mount core.zzz.nothing; // error<E402>: module `core.zzz` not found
mount core.collections.list.{NoSuchSymbol}; // error<E401>: cannot find `NoSuchSymbol` in module …

So a reader who meets "module not found" and looks up E203 finds a code the compiler will never show them, while the one they did see — E402 — is filed under Type below, described as "module not found; also Send not implemented, and …". One code, several jobs.

E202 fires for nothing because import privacy is not enforced at all: the five-level visibility table in language/modules is documentation of an intent, not of a check.

Memory — E3xx​

CodeMeaning
E310borrow conflict; also use after move, an invalid index, and a &checked reference that may escape
E311field already borrowed — cannot borrow while a field of it is
E312lifetime error
E313cannot move a value while it is borrowed
E314borrow conflict
E302affine value used in a loop
E303linear value not consumed exactly once
E304affine value used more than once
E305use of an uninitialized or partially initialized value
E306capability violation
E320stack allocation exceeds the safe limit
E321unbounded recursion detected
E370positivity violation in a recursive type

See CBGR for the three-tier reference model these diagnostics enforce.

Type — E4xx​

Four of these carry more than one meaning. The table gives the registry's own wording, because the second meaning is usually the one you will meet:

CodeMeaning
E400type mismatch
E401a name is not found in the module named; also an invalid assignment or cast
E402module not found; also Send not implemented, and mixed Int/Float arithmetic
E403infinite (self-referential) type; also Sync not implemented, and undefined function
E404inferred type is not fully determined — annotate it; also a missing protocol implementation, and an unknown field or method
E405protocol method not implemented
E406type inference failure
E407recursive type without indirection
E408dependent value-argument arity mismatch
E412value is not a function
E413const generic parameter mismatch
E414ambiguous type: not inferable without more context
E415name is not a type
E416? used outside a function
E417cycle among type definitions
E418existential type escapes its scope
E419existential bound not satisfied
E420kind mismatch
E421type constructor arity mismatch
E422associated type cannot be resolved
E423ambiguous associated type
E424negative bound violated
E425specialization overlap
E426higher-kinded bound not satisfied
E429a refutable pattern in a position that must always match

E429 — the pattern has to match​

Three positions bind exactly once against exactly one value, so a pattern used there must match every time: a function parameter (matched against whatever the caller passes), a plain let, and a comprehension's let clause (bound once per row).

let n = 5;
let 7 = n; // E429: a literal matches one value of many
let 1..=9 = n; // E429: a range, same objection
let 1 | 2 = n; // E429: an or-pattern, an alternative can fail

set{ y for x in xs let 7 = x } // E429: the clause binds once per row

The refutable case has its own form — let … else { … } — and a comprehension filters with an if clause BEFORE it binds:

let Maybe.Some(x) = v else { return; }; // fine

set{ pair.1
for email in addresses
if email.split_once(&"@") is Maybe.Some(_) // filter first
let pair = email.split_once(&"@").unwrap() } // then bind
A variant pattern is not yet caught

let Maybe.Some(x) = v; without an else is still accepted, and binds an unchecked payload — reading a field of x then dereferences null. Deciding refutability for a variant needs the resolved type, and the check that raises E429 works on the pattern's syntax: a single-variant newtype destructured by name (let UserId(n) = id;) always matches and must stay legal. Until that half lands, write let … else whenever the type has more than one variant.

A code with three meanings cannot be looked up the way this page is meant to be used — you read the message, not the number. The overload is why E200/E203 above are dead: their conditions were folded into E401/E402 and the module namespace was left standing.

E400 is the most common diagnostic in the language, and its message always names both sides. Three shapes are worth recognising:

A container is not its element.

let mut xs: List<Int> = List.new();
let y: Int = xs;
// error<E400>: Type mismatch: expected 'Int', found 'List<Int>'

Indexing yields the element — let y: Int = xs[0]; — and xs.len() yields a count. A collection never stands in for an integer. See Coercion markers for which types may stand in for which, and why containers are excluded.

A sum type is not its payload.

let m: Maybe<Int> = xs.first();
let z: Int = m;
// error<E400>: Type mismatch: expected 'Int', found 'None(Unit) | Some(Int)'

Unwrap it deliberately — match, unwrap_or, ? — so the absent case has an answer.

A Maybe wrapped twice.

fn head(xs: &List<Int>) -> Maybe<Int> {
Maybe.Some(xs.first()) // xs.first() is ALREADY Maybe<Int>
}
// error<E400>: Type mismatch: expected 'Int', found 'Maybe<Int>'

The fix is to return xs.first() directly.

E407 means a type contains itself with no indirection — a value of it would need infinite size. Insert Heap<T>, which puts the recursive occurrence behind an allocation:

type Tree is Leaf(Int) | Node { left: Heap<Tree>, right: Heap<Tree> };

let t = Tree.Node { left: Heap(Tree.Leaf(1)), right: Heap(Tree.Leaf(2)) };
match t {
Leaf(v) => print(v),
Node { left, right } => print(0),
}

Note that a recursive walk over such a tree needs the Heap opened before the child is passed on — passing left where a &Tree is expected is an E400, not an E407.

Verification — E5xx​

CodeMeaning
E500contract violated
E501invalid refinement predicate; also: a meta function declared pure has side effects
E502a meta function uses runtime contexts, which are not available at compile time
E503a pure function has side effects
E504.await used outside an async context
E505corecursive function is non-productive
E506meta argument violates its refinement

E501 is a rejection of your program, and this paragraph used to say the opposite — that the solver had run out of budget. That was the registry's wording, not the compiler's. The code has exactly two emit sites and neither mentions a timeout:

grep -rn 'invalid refinement predicate' crates/verum_types/src/lib.rs
grep -rn 'must be pure but has side effects' crates/verum_types/src/lib.rs

Grepping for the code itself is the obvious command and the wrong one: .code("E501") and the message it carries sit on different lines, so a line-oriented search shows you the code without the text or the text without the code. The two messages above are what the emit sites build.

So read it as "the predicate itself is not a valid refinement", or as a purity violation in a meta function. See Refinement types.

E504 was corrected in the same pass and for the same reason. The registry calls it "postcondition not established"; its single producer in the type checker reports

`.await` can only be used inside an async context (async fn, async block, or async closure)

which is what a reader will actually see. That one is invisible to the site's error-code gate, because the message is assembled as format!("E504: {}", message) with the text supplied from elsewhere — worth knowing if you are relying on that gate to catch this class.

Context — E6xx​

CodeMeaningEmitted?
E600context not providedyes
E601context conflictno
E602context cycleno
E603context mismatchyes
E604context not allowed hereyes
E605undefined contextyes
E606context has no such methodyes
E607invalid sub-contextyes
E608excluded context usedyes
E609transitive negative-context violationyes
E610non-context protocol in a using clauseyes
E611direct negative-context violationyes
E612context alias conflictyes
E613context used but not declared in the function signatureyes

E600 means a function declared using [Database] was called from a scope with no provide for it. See Context system.

E601 and E602 are reserved rather than live. Both used to be emitted — but for conditions belonging to other categories: E601 reported a visibility violation and E602 an ambiguous name, neither of which is a context error. They now report under the codes that already meant them, E202 and E105, so the range contract this page opens with — E1xx a name, E6xx the context system — holds for them too. Nothing emits E601 or E602 today.

Async — E7xx, FFI — E8xx, Internal — E9xx​

None of these nine has an emit site

every code below is a registry entry that no part of the compiler produces. Three whole categories are reserved rather than live.

CodeMeaningEmitted?
E700future cancelled unexpectedlyno
E701async boundary violationno
E702task join errorno
E800unsafe FFI violationno
E801ABI mismatchno
E802null pointer dereference in FFIno
E803invalid type for an inline-assembly const operandyes
E804inline-assembly output operand is not an lvalueyes
E808duplicate provide for one contextyes
E900internal compiler errorno
E901compiler assertion failedno
E902unexpected compiler stateno

They are listed because the registry has them and verum --explain will answer for them — not because a program can provoke one. An async misuse or an FFI mistake surfaces today as an ordinary E4xx type error, and an internal failure as a panic with a backtrace rather than a coded diagnostic.

make check-doc-error-codes holds this at fourteen cited-but-unemitted codes, so the list cannot quietly grow.

An E9xx, if one ever appears, is a compiler bug and never a problem with your program. Report it with the smallest input that reproduces it.

Reading a diagnostic​

Three habits pay off:

  1. Read both type names in an E400, not just the first. The message prints expected (what the context demands) then found (what the expression produced). Which of the two is wrong is your judgement, and the answer is not always "the expression".
  2. Trust the caret over the line. Context lines are shown for orientation; the ^^^^ marks the sub-expression that failed.
  3. An E1xx before an E4xx usually explains it. A name the compiler could not resolve degrades everything downstream of it. Fix the first diagnostic and re-run before reading the rest.