Skip to main content
Next (unreleased) · docs c218d875 · core f63c1e69 · built 2026-10-26 22:39 UTC

Metel Error Code Reference

All Metel errors carry a code. Codes are prefixed by phase:

PrefixPhase
PParse — invalid source text
TType — type-checker rejection
RRuntime — error during execution
IInternal — bug in the interpreter (please report)

Parse errors (P)​

P0001 — Syntax error​

The source text does not match the Metel grammar.

Fix: correct the syntax at the indicated position.

Tested by (30)

P0002 — Invalid integer literal​

An integer literal is out of range for i64 (−9,223,372,036,854,775,808 to 9,223,372,036,854,775,807).

Fix: use a value that fits in i64, or split the computation.

P0003 — Invalid float literal​

A float literal cannot be represented as an f64.

[P0003] parse error in main.mtl at 4..12: invalid float literal '1e9999'

Fix: use a value within the f64 range (~±1.8 × 10³⁰⁸).

Exempt from fixture coverage — untestable: Neither documented route is reachable: the grammar has no exponent notation, so a literal like 1e9999 is actually P0001, not P0003; and a literal long enough to overflow f64 in plain decimal notation silently saturates to infinity instead of erroring.

Type errors (T)​

T0001 — Type mismatch, or an impl that is not allowed​

Two types that must be equal are not.

When both are records, the message names the field that differs instead of printing the two types: the record is missing field y(expected{ x: i64, y: i64 }, found { x: integer }), field `z` is not part of `{ x: i64, y: i64 }` , or field `x`: expected `i64`, found `String` . An unresolved numeric literal prints as integer or float and an open tail as ... The same mismatch reached through a row bound is T0012, in the same words.

Fix: ensure the expression produces the expected type. Add an explicit cast if widening (e.g. x as f64).

A ? whose operand is Result<T, E> or Option<T> but whose enclosing function returns the other one is also this code, with the message "? on Option<String> needs the enclosing function to return Option<_>, but it returns Result<Config, ConfigError>" and a help line naming the bridge: .ok_or(...) for an Option in a Result function, .ok() for a Result in an Option function (RFC-0182).

When the expected type is the referent of a reference-typed expression (&T found where T is expected), the error carries = help: read the value out with *r (copies a Copy referent) (RFC-0185). A reference is never converted to its referent implicitly.

The same code also covers an extend block the language does not permit, which is a distinct situation sharing one code:

  • a target that cannot carry the impl at all — extend { … }: Drop on an anonymous record;
  • a target with nowhere to register, so its methods could never be found — a tuple, an anonymous record, a fun type, or an array whose element is not one of the impl's own type parameters. Only extend<T> [T]: Aspect — the array's element spelled exactly as one of the impl's own generics — is implemented today;
  • a drop body, while destructor invocation is not yet implemented.

Fix: each message names the way forward — usually a named struct, or the generic form where one exists.

Tested by (97)

T0002 — Annotation required​

The type checker cannot infer a type without an explicit annotation.

Fix: annotate the binding: let x: i64 = ....

A closure parameter written without a type is T0002 only when nothing determines it: the message is "closure parameter x has no determined type; add an annotation or give the closure an expected function type". An expected function type or the body normally fixes it.

The same code also covers dereferencing (*expr) an operand that isn't a reference type at all — not an inference gap, but sharing the code with the annotation case above since both are "the checker has nothing to work with here":

Fix: remove the *, or check that the operand actually has reference type (&T / &var T).

Tested by (2)

T0003 — Undefined name​

A name is used but not defined in the current scope. The same code reports a method call on a type that has no such method, including a function type, which implements no aspect and so has no methods (f.clone(), or "${f}", which calls to_string).

Naming Perhaps, the former name of the optional type, where nothing declares it is also this code. The message says Perhaps is now Option (Types — Option<T>).

Fix: define the variable or function before use, or correct the spelling.

Tested by (62)

T0004 — Arity mismatch​

A function is called with the wrong number of arguments.

Fix: pass the exact number of arguments the function declares.

T0005 — Invalid operand types​

An operator is applied to operands it does not support. Three forms share this code:

  • Mismatched operands. The two sides of a binary operator disagree, e.g. 1 == "x". The message names the operator and both types.
  • Binary arithmetic/ordering on unsupported types.
  • Equality (==, !=) on anything other than a numeric type, boolean, String or char. == does not yet dispatch through the Eq aspect, so structs, enums, arrays, tuples and references are rejected; use .eq(..) on a type that implements Eq.

Since v0.12.0: address-of (&, &var) applied to a non-addressable expression — a literal, a call result, a struct/enum construction — is no longer one of this code's cases. Both forms now get temporary lifetime extension instead of being rejected; see Expressions — References.

Fix: use compatible types, cast one operand, or bind the value to a name so it has an address.

Tested by (2)

T0006 — Assignment to immutable binding​

A write operation targets a let binding. This covers three forms:

  • Direct reassignment: x = newValue
  • Field assignment through an immutable binding: point.x = 1
  • Taking a mutable reference to an immutable binding: &var x

Fix: change the binding declaration to var.

Tested by (4)

T0007 — Invalid cast​

A as cast between incompatible types.

Fix: only cast between numeric types (i64 as f64). Use an explicit conversion function for other types.

T0008 — Non-exhaustive match​

A match expression does not cover all possible values of the scrutinee type.

Fix: add the missing arms, or add a wildcard arm _ => ....

Tested by (4)

T0012 — Aspect bound not satisfied​

A generic type parameter's bound is not satisfied by the concrete type at the call site or construction site. Covers both directions: a positive bound (T: Aspect) requires an implementation that isn't reachable, or a negative bound (T: !Aspect, RFC-0072) is violated because the concrete type does implement the aspect. Also covers a conditional extend block's own where-clause bounds (RFC-0036) failing at a use site — the same check as an ordinary function bound, just reached through an implementation block's condition instead of a function's generic parameter.

A type satisfying T: Copy automatically satisfies T: !Drop (RFC-0072 §2.3) even though it implements Drop — this is a narrow, Copy/Drop-specific exception, not a general rule.

Fix: implement the required aspect for the type, or (for a negative bound) remove the conflicting positive implementation.

Tested by (104)

T0013 — Ambiguous aspect method/associated-type resolution​

Two different aspects define the same method name on the same receiver type, so a call like value.method() does not have a unique static target — or (RFC-0082 §3a) two different aspects bound on the same generic type parameter both declare an associated type of the same name, so a bare projection like T::AssocName doesn't have a unique target either.

Fix (method case): rename one of the methods, remove one of the conflicting impls, or change the design so the receiver type does not expose two indistinguishable aspect methods.

Fix (associated-type case): bind the associated type to a fresh type parameter via an equality-constrained bound instead of projecting it directly — e.g. fun f<T: Deref<Target = U> + Convert, U>(x: &T) -> U — which resolves unambiguously since U is an ordinary type parameter, not a projection.

Tested by (2)

T0014 — Orphan implementation​

An extend Type: Aspect block where neither Aspect nor Type's outermost type constructor is declared in the current module (or std::core, for built-ins).

Fix: move the extend block into the module that declares the aspect or the type, or (for two foreign types) into std::core if this is genuinely a standard-library concern.

Tested by (7)

T0015 — Conflicting implementation​

Two implementations of the same aspect cover the same concrete type — either two identical extend blocks, or a positive and a negative impl (see Negative Impls in the declarations reference) for the same concrete type.

Fix: remove the duplicate extend block, or narrow one block's type arguments so the two no longer overlap.

Tested by (7)

T0016 — Non-diverging -> ! function​

A function declared -> ! (RFC-0078) contains a reachable path that doesn't diverge — most commonly an ordinary return <expr> where <expr> isn't itself !-typed. A -> ! function promises never to return; the compiler verifies every control-flow path ends in a diverging expression (a panic, a loop with no reachable break, or a return/tail expression whose own value is already !-typed).

Fix: make every path genuinely diverge (panic(msg), loop { }, or a recursive/other !-returning call), or drop the -> ! annotation if the function is meant to return normally.

T0017 — Missing associated type definition​

An extend Type: Aspect block omits a type Name = ConcreteType; definition for an associated type the aspect declares (RFC-0082 §2). Every implementation of an aspect with associated types must define all of them.

Fix: add the missing type Item = ConcreteType; definition to the extend block.

T0018 — Naming the concrete type of an opaque return value​

A function returning extends Aspect (RFC-0037) hides its concrete return type. Using the result in a position that pins it to a specific type — annotating it, or unifying it with a concrete type — defeats that, and is rejected.

Fix: keep the value opaque — annotate it as extends Aspect too, or accept it through a generic parameter with the same bound.

T0019 — Use of moved value​

Since v0.12.0, under --move-check only, with one exception: using a field that was already moved out of a nominal struct or record (reading it, or projecting it again) is reported without the flag, because the narrowing that makes the field unavailable is a type-level fact. Move checking is off by default in this release.

An ownership rule from RFC-0071 §1/§7 was violated. Eight distinct situations share this code, each with its own message:

  • a value used after it was moved;
  • a field used after it was moved out of a nominal struct or record, including a second projection of it;
  • a partially moved value used as a whole;
  • a partial move out of a type that implements Drop, which is never allowed;
  • a move out of an array element, which is banned outright;
  • a move of a non-Copy element out of a borrowed [T] view;
  • a &var binding moved by a use that is not a reborrow;
  • a value moved out of a reference — by calling a by-value self method through it, in general assignment or by-value argument position, or by reading a field through it with no explicit * at all. A reference only grants access, never ownership, so its pointee cannot be moved out this way, unless the pointee's own type is Copy (in which case the read is a copy, exactly as T: Copy already permits at read-copy positions per §3a).

Each message names the binding and the location of the move. When the move happened on an earlier iteration of an enclosing loop, the message says so — a loop-carried move is usually the same expression as the use, one iteration later, so naming only its location would point back at the line you are already reading.

Fix: depending on the rule — borrow instead of moving (&x), clone the value, move the whole value rather than a field of a Drop type, or index-and-copy rather than moving an element out of an array.

Tested by (92)

T0021 — break/continue with no enclosing loop​

break or continue appeared with no enclosing loop of any kind (loop, while, for, or for-in) to bind to. This includes a break/continue written inside a closure body — a closure is never considered to be "inside" whatever loop happens to lexically surround its definition, since the closure may be called long after that loop has exited, or from somewhere the loop never ran at all.

Fix: remove the keyword, or move it inside the loop it is meant to control. If it is meant to control a loop that encloses the call site of a closure rather than the closure's own definition, restructure the code — a closure cannot break or continue a loop it does not itself contain.

T0022 — extends Aspect outside parameter or return position​

extends Aspect was written somewhere other than a function parameter's type or a function's return type — for example, a let/var annotation, a struct or enum variant field, a cast target (x as extends P), or a generic bound. Parameter position is lowered to a fresh bounded type parameter, and return position is RFC-0037's opaque return type; every other position is not part of this language version.

Fix: name a concrete type instead, or restructure the code so the aspect bound is expressed through a parameter or return type.

Tested by (2)

T0023 — Assignment through a non-owning view​

An index assignment targets a [T] value. Since RFC-0126, [T] is an unconditionally Copy, non-owning view — it never grants write access through its indices, independent of whether the binding holding it is let or var. This is a different failure shape than T0006 (all three of T0006's forms are about a let binding that declaring it var would fix); no annotation or binding-mutability change can fix this one.

Fix: use [T; N] (a fixed-size array) or List<T> (a growable, owned collection) instead of [T] for storage that needs index-write access.

Tested by (2)

T0024 — Read-copy of a non-Copy value out of a reference​

Since v0.12.1.

Retired (RFC-0185). This code no longer fires. It rejected the implicit read-copy of a non-Copy value out of a reference (RFC-0067a §3a), a conversion that no longer exists: a reference is never read out implicitly, so let x: T := r; is T0001 with a = help: read the value out with *r`` line, for a Copy referent and a non-Copy one alike. An explicit *r of a non-Copy referent is a move out of a reference, rejected by the move checker (T0019). The number is never reused.

Fix: call .clone() if the type implements Clone, or restructure the code to take ownership of the value directly instead of reading it through a reference.

The closure cluster reserves the contiguous T0026–T0030 block, split below so each code's own coverage is visible rather than folded into one shared entry (an implementation gap in only one of the five would otherwise hide behind the other four).

T0026 — Capture list required, incomplete, or incompatible​

Availability: Since v0.13.0.

A capture list is required, incomplete, or uses an incompatible capture form.

Fix: use the capture list the closure body's captures actually require.

Tested by (8)

T0027 — Consuming capture without once​

Availability: Since v0.13.0.

A closure body consumes a capture but the literal/type is not once.

Fix: mark the closure once.

T0028 — Mutating capture without var​

Availability: Since v0.13.0.

A closure body mutates a capture, or uses [&var x], but is not var.

Fix: mark the closure var.

Tested by (2)

T0029 — var closure called through a shared reference​

Availability: Since v0.13.0.

A var closure is called through a shared reference.

Fix: call through an owned binding or an exclusive (&var) reference instead.

T0030 — Inner closure borrows an outer by-value capture​

An inner closure borrows an enclosing closure's by-value capture.

Fix: restructure the code until RFC-0122 supplies the necessary borrow analysis.

Exempt from fixture coverage — blocked on RFC-0122: requires RFC-0122's borrow analysis, not yet implemented

T0031 — Invalid program entry point​

The root module does not declare exactly one function named main, non-generic, callable with zero arguments, with a body the type checker can type (RFC-0167). Replaces retired R0001/R0002's main-entry cases — see those entries below.

Fix: add fun main() { ... } (or fix its shape) to your program's root module.

Tested by (4)

T0032 — Anonymous row in this position​

An anonymous row — the splice Builder<..> (RFC-0121 §5) or an open record with an anonymous tail, { x: T, .. } — is a fresh, nameless row variable at each occurrence. It is accepted in a function or method parameter, a let or var annotation, and a closure parameter (a closure with such a parameter generalizes, so one closure accepts records of different shapes). The splice Builder<..> is also accepted in a return type and in a written function type.

It is T0032 in a record or struct field, and an open record { x: T, .. } is T0032 in a return type and in a written function type, positions whose meaning is not settled (RFC-0181). A named splice (Builder<..R>, R declared row) and a tail-only { ..R } are supported everywhere. An undeclared row variable is T0003, and a ..R argument for a non-row parameter (or a non-record argument for a row one) is T0012 (metel-core#1310, #1478).

Fix: name the row with a row-kinded generic parameter and splice it as ..R, or take the open record as a parameter, a let annotation or a closure parameter. What works instead depends on the position, and the message says so:

  • a free function's return type: omit the return type so the exact record is inferred (private functions only; a public function needs an explicit type, T0010), write the record in full (-> { x: i64, y: i64 }), or name a row parameter when the row comes from an argument (-> { x: i64, ..R });
  • a method's return type, or an aspect method declaration's: write the record in full or name a row parameter. Leaving the type out does not infer it there: the method returns ();
  • a closure's return annotation: omit the annotation, and the exact record is inferred, or write the record in full. A closure cannot name a row parameter;
  • a written function type: share one R between the parameter and the result when the callback maps records of one shape.
Tested by (6)

T0033 — A record spread through a reference needs every field Copy​

Spreading a record through a reference ({ ..r } where r is &{ .. }) copies its fields, so it is accepted only when every field is Copy (RFC-0178). RFC-0179 removed the by-value case this code used to report: an open-row parameter now takes its whole argument, so nothing is forgotten at a call.

Fix: make the fields Copy, clone the record explicitly, or spread an owned record.

Runtime errors (R)​

T0034 — Duplicate method definition​

A method name is defined twice where both definitions can apply to the same receiver: twice in one extend block (inherent or an aspect impl), twice in one aspect declaration, or in two inherent extend blocks of one type whose coverage overlaps. Without this error the later definition would silently replace the earlier one at dispatch. Methods are not overloaded, so different parameter types do not make two same-named methods distinct.

Blocks whose coverage is provably disjoint may reuse a name — extend W<i64> and extend W<String>, bounds that contradict (T: Tag and T: !Tag), or row bounds that contradict on a shared label (row R: { token, .. } and row R: !{ token }) — and a call dispatches to the block the receiver satisfies. The overlap test is the one T0015 applies to aspect impls.

Fix: rename one of the methods, merge the two definitions, or narrow one block so the two no longer overlap.

Tested by (6)

T0035 — Use not granted by the declared bounds​

A generic definition used its type parameter in a way its declared bounds do not grant (RFC-0173): a method call on a parameter that has no bounds, or whose bounds do not declare the method, or a field access on a parameter with no row bound that names the field. The message names the parameter and, for a method, the bounds it has.

aspect Show { fun show(self) -> i64; }
fun f<T>(x: T) -> i64 { x.show() } // T has no Show bound

Inside its own definition a declared parameter is opaque: it supports only what its bounds entail. Passing it where a callee requires a bound it lacks is T0012; an operator on a bare parameter is T0005; using it as a concrete type, or as another parameter, is T0001.

Fix: add the bound that grants the use (fun f<T: Show>(x: T)), or take a concrete type.

Tested by (7)

T0036 — By-value open-row parameter over a Drop brand​

A by-value open-row parameter over a nominal type that has a Drop impl (fun f<row R>(h: Handle.{ fd, ..R }) given a Handle) would drop a narrowed Handle.{ fd } in the callee. Drop dispatch against a narrowed residual is not implemented (RFC-0137 §5, #949), so the call is rejected (RFC-0179 §1.5). A plain record that only contains a Drop value is not affected.

Fix: take the value by reference, or give the parameter the whole type.

T0037 — Duplicate top-level declaration​

A top-level name is declared twice in one namespace of one module. Structs, enums, aspects and type aliases share the type namespace; functions, let and var share the value namespace. A type and a value may share a name (struct Foo and fun Foo coexist); two types, or a binding and a function, may not. Two funs of one name are an overload set, so repeating a function is T0011 only when their parameter types are identical. Without this error the second declaration shared the first one's identity and went unseen.

let _ := ... binds nothing, so repeating it is allowed.

Fix: rename one of the declarations, or merge them.

Tested by (5)

T0038 — Declaring a type with the name of an intrinsic type​

A struct, enum, aspect, type alias, or block-local struct or enum is declared with the name of an intrinsic type: boolean, String, Char, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, Range or RangeInclusive. The language provides these types and std::core declares none of them, so a user type of the same name would capture part of the compiler's lookups by name: with struct String { ... } declared, println of an ordinary string failed because the Display impl belongs to the primitive. Only the type namespace is reserved. A function or let may use the name, and the types std::core declares (List, Option, Result, ...) are not covered.

Fix: rename the type.

Tested by (5)

R0001 — No main function defined​

Retired, replaced by T0031 (RFC-0167). The main-existence check now runs during typechecking, not lazily at evaluator startup; this code no longer fires. Kept documented, never reused, per this project's rigor-block-id convention (STYLEGUIDE.md) — see T0031 above.

Formerly: execution required a main function but none was found.

R0002 — main is not a valid entry point​

Retired, split three ways by RFC-0167. This code no longer fires: main exists but has the wrong shape is now T0031 above; an ordinary (non-main) call on a non-callable value is now I0007 below (merged with what R0010 became); a generic closure/method with no call-site type context is now I0009 below, its own code, unrelated to main. Kept documented, never reused, per this project's rigor-block-id convention (STYLEGUIDE.md).

Formerly: main existed but was generic or was not a function.

R0003 — Undefined variable at runtime​

Retired, replaced by I0003 (RFC-0167). This code no longer fires. Kept documented, never reused, per this project's rigor-block-id convention (STYLEGUIDE.md) — see I0003 below.

Formerly: a variable name was not found in the current environment.

R0004 — Index out of bounds​

An array index is negative or ≥ the array length.

Fix: check that the index is within 0..array.len() before access.

R0005 — Tuple index out of bounds​

A tuple element is accessed by an index that does not exist.

[R0005] runtime error in main.mtl at 5..10: tuple index 3 out of bounds

Fix: tuple indices are fixed at compile time; verify the index against the tuple's declared length.

Note: not confirmed reachable from ordinary source. A tuple index is always a literal token, never a computed expression, so an out-of-range index was caught as T0003 statically in every construction tried. Unlike P0003 above, the raise site is real code — just unconfirmed.

Exempt from fixture coverage — untestable: Checked and found unreachable via ordinary source -- tuple indices are fixed at compile time and out-of-range access is caught statically, not deferred to runtime.

R0006 — Non-exhaustive match at runtime​

Retired, replaced by I0004 (RFC-0167). This code no longer fires. Kept documented, never reused, per this project's rigor-block-id convention (STYLEGUIDE.md) — see I0004 below.

Formerly: a match expression reached its end without any arm matching.

R0007 — Arithmetic error​

Integer division or remainder by zero, or integer overflow on +, -, *, or / (RFC-0007 D3, amended 2026-08-26 — panics unconditionally in every build; there is no debug/release distinction). Floating-point arithmetic never raises this code — float overflow and division by zero follow IEEE 754 (inf/-inf/NaN), never a panic.

Fix: guard with a zero check before dividing, or ensure operands stay in range before an operation that could overflow.

R0008 — Field not found​

Retired, replaced by I0005 (RFC-0167). This code no longer fires. Kept documented, never reused, per this project's rigor-block-id convention (STYLEGUIDE.md) — see I0005 below.

Formerly: a struct or enum value did not have the accessed field.

R0009 — Method not found​

Retired, replaced by I0006 (RFC-0167). This code no longer fires. Kept documented, never reused, per this project's rigor-block-id convention (STYLEGUIDE.md) — see I0006 below.

Formerly: a method call could not be resolved for the receiver type.

R0010 — Call on non-callable value​

Retired, replaced by I0007 (RFC-0167). This code no longer fires, and merges with R0002's ordinary (non-main) call-target case. Kept documented, never reused, per this project's rigor-block-id convention (STYLEGUIDE.md) — see I0007 below.

Formerly: a call expression (f(...)) was applied to a value that is not a function or closure.

R0011 — Invalid for-in iterator​

Retired, replaced by I0008 (RFC-0167). This code no longer fires. Kept documented, never reused, per this project's rigor-block-id convention (STYLEGUIDE.md) — see I0008 below.

Formerly: a for x in expr loop where expr did not evaluate to an Array, a Range, or a type implementing Iterable.

R0012 — Assertion failed​

assert(cond) or assert(cond, msg) is called with cond evaluating to false. The panic message is the fixed string "assertion failed" for the one-argument form, or the caller-supplied msg for the two-argument form.

Fix: this is not a bug in the interpreter — it means the asserted condition was actually false at runtime. Fix the condition, or the code that led to it.

Tested by (2)

R0013 — Unwrap on None/Err​

.yolo() is called on an Option<T> that is None, or a Result<T, E> that is Err. For Result, the panic message includes the Err value's debug representation.

Fix: this is not a bug in the interpreter — .yolo() is meant only for cases where None/Err represents a logic error that should never occur in correct code. Use match, .unwrap_or, .unwrap_or_else, or (for Result) ? to handle the expected case instead.

R0014 — Explicit panic​

panic(msg) (RFC-0078) is called. Always panics unconditionally with msg.

Fix: this is not a bug in the interpreter — panic is meant for logic errors that should never occur in correct code. Handle the expected case with ordinary control flow instead of reaching the panic call.

R0015 — Re-entrant mutating closure call​

Availability: Since v0.13.0.

A var closure tried to call the same closure value again before its current invocation finished. This is an uncatchable assertion-class runtime error.

Fix: restructure the callback/control flow so a mutating closure is not re-entered.

R0016 — Invalid Unicode scalar value​

Char::from(n) is called with a u32 that is not a Unicode scalar value, including a surrogate or a value above 0x10FFFF.

Fix: pass a Unicode scalar value to Char::from.

Internal errors (I)​

I0001 — Internal interpreter error​

The interpreter reached an impossible state. This is a bug in the interpreter — the typechecker should have caught it before execution.

[I0001] internal error: binop: unsupported operand types (typechecker should have caught this)

What to do: please file a bug report at the Metel issue tracker with the source program that triggered this error.

Exempt from fixture coverage — untestable: Forcing an internal-error state deliberately isn't meaningfully the same kind of check as an ordinary trigger -- a real repro would mean finding an actual interpreter bug, not demonstrating a language rule (not attempted).

I0002 — Not implemented​

The program uses a language feature that is not yet supported in this version of the interpreter.

[I0002] internal error: generic functions are not supported in v0.1

What to do: check the changelog for the current supported feature set and the release plan for the planned implementation milestone.

Note: I0002 and its not_implemented() constructor are kept as scaffolding — the intended way to report a recognized but not-yet-built construct while a feature is under development. There is no live raise site today. metel-core#992 tracks removing the variant and constructor if they stay unused.

Exempt from fixture coverage — untestable: Kept as scaffolding for reporting a recognized-but-unimplemented construct during feature development; there is no live raise site today, so nothing to trigger. metel-core#992 tracks removal if it stays unused.

I0003 — Scoping/hoisting invariant violated​

A variable name was not found in the current environment at runtime (RFC-0167). Whether a name resolves is a purely static scoping fact; if this fires, the type checker approved a program it should not have. Replaces retired R0003 (see above).

What to do: please file a bug report at the Metel issue tracker with the source program that triggered this error.

Exempt from fixture coverage — blocked on metel-core#986: Confirmed live raise sites (lvalue.rs, mod.rs), carried over unchanged from retired R0003's own finding. #986's follow-up round tried #712's exact precedent (nested-fun forward reference) across 5 more statement positions -- var initializer, if-branch, call-argument, match-arm, struct-literal-field, while-condition -- all resolved correctly, meaning #712's original let-initializer fix was thorough, not narrow. Still no repro found across either investigation round, and still not confirmed unreachable either.

I0004 — Exhaustiveness-checker invariant violated​

A match expression reached its end without any arm matching (RFC-0167). Whether a match's arms are exhaustive is a purely static property of the pattern set against the scrutinee type; if this fires, check_match_exhaustiveness approved a match it should not have. Replaces retired R0006 (see above).

What to do: please file a bug report at the Metel issue tracker with the source program that triggered this error.

Exempt from fixture coverage — blocked on metel-core#986: A known limitation (the type checker approving a match as exhaustive when it is not), carried over unchanged from retired R0006's own finding. #986's follow-up round read check_match_exhaustiveness end to end (typechecker/construction/patterns.rs) -- the Boolean/Named-enum/Never/SizedArray cases, is_variant_uninhabited's RFC-0078 uninhabited-payload check, and pattern_covers_variant's enum+variant name matching all look sound on inspection. No construction attempted this round (unlike R0003/R0009) since no plausible gap surfaced worth testing against.

I0005 — Field-resolution invariant violated​

A struct or enum value does not have the accessed field (RFC-0167). Whether a type has a given field is a structural fact, checked at the point of the struct/enum declaration; if this fires, that check was wrong. Replaces retired R0008 (see above).

What to do: please file a bug report at the Metel issue tracker with the source program that triggered this error.

Exempt from fixture coverage — blocked on metel-core#986: Confirmed live raise site, carried over unchanged from retired R0008's own finding, but every attempted repro (a generic function reading an unconstrained field) was caught statically as T0002 instead. #986's follow-up round: field access resolves directly against the accessed value's own concrete fields (lvalue.rs's TypedPlace::Field), not through a bare-name-keyed lookup table the way aspect methods do -- so R0009's collision bug (metel-core#989) doesn't obviously carry over here. No new construction attempted this round.

I0006 — Method-dispatch invariant violated​

A method call cannot be resolved for the receiver type (RFC-0167). Whether a type has a given method is a structural fact, checked at the point of the relevant extend block; if this fires, that check was wrong. Replaces retired R0009 (see above).

What to do: please file a bug report at the Metel issue tracker with the source program that triggered this error.

Exempt from fixture coverage — blocked on metel-core#989: Confirmed live raise site, carried over unchanged from retired R0009's own finding. #986's follow-up round found a real root-cause bug in this exact dispatch machinery: metel-core#989, two same-named aspects in different modules corrupt each other's dispatch resolution (TypeRegistry::aspect_decl_modules is keyed by bare aspect name, not a qualified path). In the variant tried (colliding aspects with differently-named methods) this surfaced as a false static T0003 rejection, not this raise site -- construction-time method lookup apparently consults the same corrupted map before dispatch is ever elaborated. A same-method-name variant (also tried) resolved correctly in both import orderings tried, so this mechanism isn't yet confirmed to reach this code specifically -- revisit once #989 is fixed.

I0007 — Callability invariant violated​

A call expression (f(...)) is applied to a value that is not a function or closure (RFC-0167). Whether a value is callable is a static type fact; in v0.13.0 there is not even a dynamic-dispatch mechanism (dyn Callable, RFC-0161) that could make it otherwise. If this fires, the type checker approved a call it should not have. Replaces retired R0010, merged with retired R0002's ordinary (non-main) call-target case (see both above) — both raise sites make the exact same claim, just from different call paths.

Note: if RFC-0161 (dyn Callable) ships, this code's classification should be revisited — a dynamically-dispatched call target's callability would then genuinely depend on a runtime value again.

What to do: please file a bug report at the Metel issue tracker with the source program that triggered this error.

Exempt from fixture coverage — blocked on metel-core#986: Confirmed live raise sites (merged from retired R0010's own finding and R0002's non-main call-target case), but calling a plain i64 variable was caught statically as T0001. #986's follow-up round: calling a value generically/dynamically has no route to try at all in v0.13.0 -- RFC-0161's dyn Callable / Callable aspect (the mechanism that would make a call target's callability depend on a runtime value rather than a static function type) is deferred in full to a later milestone, so there is currently no dynamic-dispatch angle to test against this code.

I0008 — Iterator-dispatch invariant violated​

A for x in expr loop where expr does not evaluate to an Array, a Range, or a type implementing Iterable (RFC-0167). Whether a type implements Iterable is a static bound fact, checked wherever the iteration is typed; if this fires, that check was wrong. Replaces retired R0011 (see above).

What to do: please file a bug report at the Metel issue tracker with the source program that triggered this error.

Exempt from fixture coverage — blocked on metel-core#986: Confirmed live raise sites (evaluator/mod.rs), carried over unchanged from retired R0011's own finding, but a plain non-iterable typed value (e.g. for (x in n) where n: i64) is caught statically as T0001 before reaching this runtime path. #986's follow-up round: the user-defined-Iterable dispatch this code guards resolves through the receiver value's own runtime type id (resolve_value_type_id + get_regular_method), not a bare-name-keyed table -- unlike R0009's aspect-method path (metel-core#989), this one isn't obviously vulnerable to the same class of collision bug. No construction attempted this round on that basis.

I0009 — Generic construction-at-call-time invariant violated​

A generic closure or method's construction-at-call-time machinery (metel-core#286) was invoked with no call-site type context available (RFC-0167). Unrelated to call-target shape or main entirely — a construction-machinery plumbing failure, not a callability question. Replaces retired R0002's other orphan use (see above); this is a wholly new code, not a merge into any existing one.

What to do: please file a bug report at the Metel issue tracker with the source program that triggered this error.

Exempt from fixture coverage — untestable: This code is new with RFC-0167 -- no prior R0002-era reachability finding to carry over. Its two raise sites (call.rs) fire only when a generic closure/method's own captured type_ctx is None, which every construction path that builds a ClosureBody::Untyped value populates unconditionally (Pass 1b and hoist_nested_funs both always pass env.type_ctx.clone()); no construction was found that reaches ClosureBody::Untyped with type_ctx already None.

I0010 — Generic definition and construction disagree​

The checker accepted a generic definition, but reconstructing that generic body for a concrete call rejected it (RFC-0173). The definition-time check is the contract, so this signals an implementation invariant violation rather than an error at the call site.

What to do: please file a bug report at the Metel issue tracker with the source program that triggered this error.

Exempt from fixture coverage — untestable: I0010 reports a disagreement between two checker passes. No source fixture can correctly require such an implementation-invariant failure; the differential check that exercises the invariant remains tracked by metel-core#1364.

I0011 — Move checking could not analyze a body​

Under --move-check, the move checker could not analyze at least one generic body, so it cannot vouch for the program (ADR-0063, abstract generic bodies). This is not a move violation: nothing was shown to be used after a move. It is reported as its own error because an incomplete analysis must never authorize execution, and a program that passes --move-check has had every body checked. Default mode, which does not run the move checker, is unaffected.

What to do: please file a bug report at the Metel issue tracker with the source program that triggered this error.

Exempt from fixture coverage — untestable: I0011 is a defensive guard against an implementation gap or a feature that lacks ownership integration. Every valid body in the implemented language is analyzed, so no source fixture can correctly require it. The guard is exercised by unit tests that remove a body's retained facts, and a corpus test asserts that no fixture reaches it.