Appendix C: Error Codes

Every diagnostic the Ori compiler emits carries a unique, permanent error code. This appendix is the canonical reference for all codes across every compiler phase. It covers the code format and numbering scheme, the complete table of all 119 codes, representative diagnostic output from each major phase, the ori --explain documentation system, and the procedure for adding new codes.

How to Use This Reference

The fastest way to understand any error is the --explain flag:

ori --explain E2001

This prints the full documentation for the code: a title, an explanation of what went wrong, annotated Ori code examples that trigger the error, concrete solutions, and cross-references to related codes. Every documented code follows this format.

Error code format

Codes follow the pattern <prefix><phase><sequence>:

  • Prefix: E for errors, W for warnings.
  • Phase digit: the first digit after the prefix identifies the compiler phase that emits the diagnostic.
  • Sequence: a three-digit number, allocated sequentially within each phase range. Gaps in the sequence (such as the absence of E0007) indicate codes that were reserved during development but never assigned.

Stability guarantee

Once a code is assigned, its meaning never changes. A code may be deprecated (no longer emitted by the compiler), but it will never be reassigned to a different diagnostic. This guarantee means that external tooling, CI scripts, and documentation can refer to error codes by number and trust that E2001 will always mean “type mismatch.”

Error Code Ranges

Each compiler phase owns a contiguous range. The phase digit makes it possible to identify the origin of any diagnostic at a glance.

RangePhaseDescription
E0xxxLexerTokenization errors: malformed literals, invalid characters, cross-language habits
E1xxxParserSyntax errors: unexpected tokens, unclosed delimiters, invalid declarations
E2xxxType CheckerType errors: mismatches, missing bounds, inference failures, capability violations
E3xxxPatternsPattern system errors: unknown patterns, invalid arguments, type mismatches
E4xxxARC AnalysisARC IR lowering errors: unsupported constructs, FBIP violations
E5xxxCodegen / LLVMCode generation errors: verification failures, linker errors, target issues
E6xxxRuntime / EvalEvaluator runtime errors: arithmetic faults, lookup failures, control flow
E9xxxInternalCompiler bugs and infrastructure limits
W1xxxParser WarningsNon-fatal parser diagnostics
W2xxxType WarningsNon-fatal type checker diagnostics

Import and module resolution errors fall under the E2xxx range because they are detected during type checking, not during a separate import resolution phase. See the Import and Module Errors section for details.

Complete Error Code Table

The table below lists every code defined in the define_error_codes! macro. The Docs column indicates whether the code has a detailed documentation file accessible through ori --explain.

Lexer Errors (E0xxx)

CodeNameDescriptionDocs
E0001Unterminated StringString literal not closed before end of line or fileyes
E0002Invalid CharacterCharacter not valid in Ori source textyes
E0003Invalid NumberMalformed numeric literal (e.g., multiple decimal points)yes
E0004Unterminated CharCharacter literal not closedyes
E0005Invalid EscapeUnrecognized escape sequence in string or character literalyes
E0006Unterminated TemplateTemplate literal (backtick string) not closed
E0008Triple-EqualsCross-language habit: === is not valid in Ori
E0009Single-Quote StringCross-language habit: single-quoted strings are not valid in Ori
E0010Increment/DecrementCross-language habit: ++/-- operators do not exist in Ori
E0011Unicode ConfusableA Unicode character that visually resembles an ASCII operator or letter
E0012Detached Doc CommentDoc comment not attached to any declaration
E0013Standalone BackslashBackslash outside of a string or character literal
E0014Decimal Not RepresentableDecimal duration/size literal cannot be represented as whole base units
E0015Reserved-Future KeywordA future-reserved keyword (asm, inline, static, union, view) used as identifier
E0911Float Duration/SizeFloating-point syntax used for a duration or size literal, which requires integer syntax

Parser Errors (E1xxx)

CodeNameDescriptionDocs
E1001Unexpected TokenParser encountered a token it did not expect in the current contextyes
E1002Expected ExpressionAn expression was expected but not foundyes
E1003Unclosed DelimiterMissing closing ), ], or }yes
E1004Expected IdentifierAn identifier was expected (e.g., after let, in a function name position)yes
E1005Expected TypeA type annotation was expected but not foundyes
E1006Invalid FunctionFunction definition is syntactically invalidyes
E1007Missing Function BodyFunction declaration has no body expressionyes
E1008Invalid Pattern SyntaxMalformed pattern in a match arm or let bindingyes
E1009Missing Pattern ArgA required argument is missing from a pattern invocationyes
E1010Unknown Pattern ArgAn argument name not recognized by the target patternyes
E1011Named Args RequiredMulti-argument function call requires named argumentsyes
E1012Invalid Block ExpressionSyntax error inside a block expression (function_seq)yes
E1013Named Properties RequiredControl flow expression (function_exp) requires named propertiesyes
E1014Reserved NameAttempt to define a function with a reserved built-in nameyes
E1015Unsupported KeywordA keyword from another language (e.g., return) that is not valid in Oriyes
E1016Expected SemicolonExpected ; after an item declaration

Type Checker Errors (E2xxx)

CodeNameDescriptionDocs
E2001Type MismatchExpression type does not match expected typeyes
E2002Unknown TypeType name not defined in the current scopeyes
E2003Unknown IdentifierName not found in the current scope (variables, functions, imports)yes
E2004Arg Count MismatchWrong number of arguments supplied to a function callyes
E2005Cannot InferType inference failed; explicit annotation requiredyes
E2006Duplicate DefinitionA name is defined more than once in the same scopeyes
E2007Closure Self-RefA closure captures itself, creating an infinite typeyes
E2008Cyclic TypeType definition contains a cycle (e.g., type T = [T])yes
E2009Missing Trait BoundA required trait is not implemented for the given typeyes
E2010Coherence ViolationConflicting trait implementations for the same typeyes
E2011Named Args RequiredNamed arguments are required for this function callyes
E2012Unknown CapabilityCapability name not recognizedyes
E2013Provider MismatchProvider value does not implement the expected capability traityes
E2014Missing CapabilityFunction uses a capability that is not declared in its uses clauseyes
E2015Type Param OrderNon-default type parameter appears after a default type parameteryes
E2016Missing Type ArgRequired type argument not supplied and no default existsyes
E2017Too Many Type ArgsMore type arguments supplied than the type acceptsyes
E2018Missing Assoc TypeImplementation is missing a required associated typeyes
E2019Never Type FieldNever used as a struct field type, which is uninhabitedyes
E2020Unsupported OperatorOperator not supported for the operand typeyes
E2021Overlapping ImplsTwo implementations overlap with equal specificityyes
E2022Conflicting DefaultsMultiple super-traits provide conflicting default methodsyes
E2023Ambiguous MethodMethod call could resolve to multiple implementationsyes
E2024Not Object-SafeTrait cannot be used as a trait objectyes
E2025Missing Index ImplType does not implement the Index traityes
E2026Wrong Index KeyKey type does not match any Index implementation for the target typeyes
E2027Ambiguous Index KeyMultiple Index implementations match the key typeyes
E2028Default Sum TypeCannot derive Default for a sum type (no obvious default variant)yes
E2029Hashable Needs EqCannot derive Hashable without also deriving or implementing Eqyes
E2030Hash InvariantHashable implementation may violate the hash consistency invariantyes
E2031Non-Hashable KeyType used as a map key does not implement Hashableyes
E2032Derive Field MissingA field type does not implement a trait required by a #derive annotationyes
E2033Non-Derivable TraitThe named trait cannot be automatically derivedyes
E2034Invalid Format SpecFormat specification in a template string is syntactically invalidyes
E2035Format Type UnsupportedFormat type specifier not supported for the expression’s typeyes
E2036Missing IntoType does not implement Into<T> for the target typeyes
E2037Ambiguous IntoMultiple Into implementations could applyyes
E2038Missing PrintableType does not implement Printable, required for template interpolationyes
E2039Immutable AssignmentCannot assign to an immutable binding (declared with let $)
E2040Feature Not SupportedLanguage feature used that is not yet supported by the compiler

Pattern Errors (E3xxx)

CodeNameDescriptionDocs
E3001Unknown PatternPattern name not recognized by the pattern registryyes
E3002Invalid Pattern ArgsArguments to a pattern are invalid (wrong names, types, or count)yes
E3003Pattern Type ErrorPattern does not match the type of the scrutineeyes

ARC Analysis Errors (E4xxx)

CodeNameDescriptionDocs
E4001Unsupported ARC ExprExpression form cannot be lowered to ARC intermediate representation
E4002Unsupported ARC PatternPattern form cannot be lowered to ARC intermediate representation
E4003ARC Internal ErrorInvariant violation in the ARC analysis pass (compiler bug)
E4004FBIP EnforcementFBIP (Functional But In-Place) constraint violated in ARC IR

Codegen / LLVM Errors (E5xxx)

CodeNameDescriptionDocs
E5001LLVM VerificationLLVM module verification failed (internal compiler error)
E5002Optimization FailedLLVM optimization pipeline encountered a fatal error
E5003Emission FailedObject file, assembly, or bitcode emission failed
E5004Target Not SupportedRequested compilation target is not available or misconfigured
E5005Runtime Not FoundRuntime library (libori_rt.a) not found at the expected path
E5006Linker FailedSystem linker returned a non-zero exit code
E5007Debug Info FailedDWARF or other debug info could not be generated
E5008WASM ErrorError specific to the WebAssembly compilation target
E5009Module Target ErrorModule-level target configuration could not be applied

Runtime / Eval Errors (E6xxx)

Runtime errors are organized into subcategories by the nature of the fault. These codes are emitted by the interpreter (evaluator) and correspond to structured EvalErrorKind variants.

Arithmetic errors (E6001—E6006)

CodeNameDescriptionDocs
E6001Division By ZeroInteger or float division where the divisor is zero
E6002Modulo By ZeroModulo operation where the divisor is zero
E6003Integer OverflowArithmetic operation overflowed the int (i64) range
E6004Size SubtractionSize subtraction would produce a negative value
E6005Size MultiplySize multiplied by a negative value
E6006Size DivideSize divided by a negative value

Type errors at runtime (E6010—E6012)

CodeNameDescriptionDocs
E6010Runtime Type MismatchValue has wrong type at runtime (should not occur after type checking)
E6011Invalid Binary OpBinary operator not valid for the operand types
E6012Binary Type MismatchLeft and right operands of a binary operator have different types

Lookup errors (E6020—E6027)

CodeNameDescriptionDocs
E6020Undefined VariableVariable name not found in the current environment
E6021Undefined FunctionFunction name not found in the current environment
E6022Undefined ConstantConstant name not found in the current environment
E6023Undefined FieldField access on a struct or record with no such field
E6024Undefined MethodMethod call on a type that does not have the named method
E6025Index Out Of BoundsList index is negative or greater than or equal to the list length
E6026Key Not FoundMap key lookup returned no entry
E6027Immutable BindingAttempt to mutate a binding declared with let $

Call errors (E6030—E6032)

CodeNameDescriptionDocs
E6030Arity MismatchFunction called with wrong number of arguments
E6031Stack OverflowRecursion depth exceeded the interpreter’s limit
E6032Not CallableAttempted to call a value that is not a function

Control flow errors (E6040—E6051)

CodeNameDescriptionDocs
E6040Non-Exhaustive MatchMatch expression did not cover the actual value
E6050Assertion FailedAn assert, assert_eq, or related assertion function failed
E6051Panic CalledExplicit panic(msg:) was invoked

Other runtime errors (E6060—E6099)

CodeNameDescriptionDocs
E6060Missing CapabilityA capability required at runtime was not provided via with...in
E6070Const-Eval BudgetConstant evaluation exceeded its computational budget
E6080Not ImplementedA language feature reached at runtime that is not yet implemented
E6099Custom Runtime ErrorA runtime error that does not fit any other category

Internal Errors (E9xxx)

CodeNameDescriptionDocs
E9001Internal ErrorAn internal compiler error (ICE); indicates a bug in the compiler itselfyes
E9002Too Many ErrorsThe error limit was reached; remaining diagnostics suppressedyes

Parser Warnings (W1xxx)

CodeNameDescriptionDocs
W1001Detached Doc CommentDoc comment is not attached to any declaration
W1002Unknown Calling ConvUnrecognized calling convention in an extern block

Type Checker Warnings (W2xxx)

CodeNameDescriptionDocs
W2001Infinite IteratorInfinite iterator (e.g., repeat, unbounded range) consumed without .take()yes

Example Diagnostics

This section shows representative diagnostic output from each major compiler phase. All diagnostics follow a consistent format: the severity and code on the first line, a source snippet with annotated spans, and optional notes or help text.

Lexer: E0001 — Unterminated String

error[E0001]: unterminated string literal
 --> src/main.ori:3:19
  |
3 |     let name = "hello
  |                 ^ string literal never closed
  |
  = help: add a closing `"` to terminate the string

The lexer detects that a double-quote was opened but never closed before the end of the line. This is one of the most common errors for programmers new to any language. The span points to the opening quote, and the help text states the fix directly.

Parser: E1001 — Unexpected Token

error[E1001]: unexpected token
 --> src/main.ori:5:11
  |
5 |     let x + = 1
  |           ^ expected `=` or `:`, found `+`

The parser expected either an = (for a binding) or a : (for a type annotation) after the identifier x, but found + instead. The label tells the programmer what was expected and what was found, which is usually enough to identify the typo.

Type Checker: E2001 — Type Mismatch

error[E2001]: type mismatch
 --> src/main.ori:3:18
  |
3 |     let x: int = "hello"
  |            ---   ^^^^^^^ expected `int`, found `str`
  |            |
  |            expected due to this annotation

This is the most frequently encountered type error. The primary label points to the expression that has the wrong type. A secondary label points to the annotation or context that established the expectation. Ori performs no implicit conversions, so the programmer must use an explicit conversion function or change the annotation.

Patterns: E3002 — Invalid Pattern Arguments

error[E3002]: missing required argument `transform` for pattern `map`
 --> src/main.ori:4:5
  |
4 |     map(over: items)
  |     ^^^^^^^^^^^^^^^^ missing `transform`
  |
  = help: valid arguments are: `over`, `transform`

Pattern expressions in Ori use named arguments. When a required argument is missing, the diagnostic names it explicitly and lists all valid arguments in the help text.

Codegen: E5005 — Runtime Not Found

error[E5005]: runtime library `libori_rt.a` not found
  |
  = note: searched at: /home/user/.local/lib/ori/libori_rt.a
  = help: build the runtime with `cargo b` or `cargo b --release`

This error appears when attempting AOT compilation without having built the Ori runtime library first. Unlike most diagnostics, it has no source span because it is not triggered by any particular line of user code. The help text gives the exact command to resolve the issue.

Runtime: E6001 — Division By Zero

error[E6001]: division by zero
 --> src/main.ori:5:15
  |
5 |     let r = x / 0
  |               ^ division by zero

Ori integers use checked arithmetic. Division by zero is a runtime error (not undefined behavior). The evaluator traps the operation and reports the exact location.

Runtime: E6025 — Index Out Of Bounds

error[E6025]: index out of bounds
 --> src/main.ori:4:13
  |
4 |     items[10]
  |          ^^^^ index 10 is out of range for list of length 3

List indexing in Ori is bounds-checked. The diagnostic includes both the index value and the list length, which are the two pieces of information needed to understand why the access failed.

Type Checker: E2009 — Missing Trait Bound

error[E2009]: missing trait bound
 --> src/main.ori:3:22
  |
3 |     let sorted = items.sort()
  |                        ^^^^ `sort` requires `Comparable`, but `MyStruct` does not implement it
  |
  = help: add `#derive(Comparable)` to `MyStruct`, or implement `Comparable` manually

When a method requires a trait that the receiver type does not implement, the diagnostic names both the method and the missing trait. If the trait is derivable, the help text suggests #derive.

Error Documentation System

The ori --explain command

Any error code can be queried from the command line:

ori --explain E2009

This prints the full markdown documentation for E2009 to the terminal. The command accepts codes case-insensitively (e2009 and E2009 both work) and returns a non-zero exit code if the code is unknown or undocumented.

How documentation is embedded

Each documented error code has a markdown file in the compiler/ori_diagnostic/src/errors/ directory, named after the code (e.g., E2001.md). These files are compiled into the binary at build time using Rust’s include_str! macro. The ErrorDocs struct provides O(1) lookup through a lazily-initialized HashMap:

// In compiler/ori_diagnostic/src/errors/mod.rs
static DOCS: &[(ErrorCode, &str)] = &[
    (ErrorCode::E0001, include_str!("E0001.md")),
    (ErrorCode::E2001, include_str!("E2001.md")),
    // ... one entry per documented code
];

This approach means documentation is always in sync with the binary that ships it. There is no separate documentation artifact to keep up to date and no risk of version skew between the compiler and its error explanations.

Standard documentation format

Every error documentation file follows a consistent structure:

  1. Title: # EXXXX: Short Name
  2. Opening paragraph: one or two sentences describing the error in plain language.
  3. Example: an annotated Ori code snippet that triggers the error.
  4. Explanation: a longer discussion of why the error occurs and what the compiler is checking.
  5. Common Causes: a numbered list of typical programmer mistakes that lead to this error.
  6. Solutions: concrete code examples showing how to fix each common cause.
  7. See Also: cross-references to related error codes.

Not every file includes every section (some errors are simple enough that “Common Causes” and “Solutions” collapse into the explanation), but the ordering is always consistent.

Documentation coverage

Of the 119 error codes currently defined, 64 have full documentation accessible through ori --explain. Coverage varies by phase:

PhaseTotal CodesDocumentedCoverage
Lexer (E0xxx)15533%
Parser (E1xxx)161594%
Type Checker (E2xxx)403895%
Patterns (E3xxx)33100%
ARC (E4xxx)400%
Codegen (E5xxx)900%
Runtime (E6xxx)2400%
Internal (E9xxx)22100%
Warnings (Wxxx)3133%
Total1196454%

The parser and type checker, which produce the errors programmers encounter most frequently, have near-complete documentation. The ARC, codegen, and runtime phases are infrastructure-facing and produce errors that are either internal (ARC, codegen) or have short self-explanatory messages (runtime arithmetic faults). Documentation for these phases is a lower priority but is planned.

Adding a New Error Code

When a new diagnostic is needed, follow these steps in order:

1. Choose the correct range

Identify which compiler phase will emit the diagnostic. Use the range table at the top of this appendix. If the error could plausibly belong to two phases, assign it to the phase that detects it. For example, import resolution errors belong to E2xxx because they are detected during type checking, even though they concern the module system.

2. Allocate the next sequence number

Within the chosen range, find the highest currently allocated code and increment by one. Gaps in the sequence are acceptable and should not be backfilled. The define_error_codes! macro in compiler/ori_diagnostic/src/error_code/mod.rs is the single source of truth.

3. Add the code to the macro

Add a new line to the define_error_codes! invocation:

define_error_codes! {
    // ...existing codes...
    E2041, "Description of the new error";
}

The macro generates the enum variant, its as_str() representation, and its description(). Update the COUNT assertion in the test file to reflect the new total.

4. Write a documentation file

Create compiler/ori_diagnostic/src/errors/E2041.md following the standard format described in the previous section. Then register it in compiler/ori_diagnostic/src/errors/mod.rs:

static DOCS: &[(ErrorCode, &str)] = &[
    // ...existing entries...
    (ErrorCode::E2041, include_str!("E2041.md")),
];

5. Use the code in the emitting phase

Each compiler phase has its own problem type or error factory. The new code is used through the diagnostic builder:

Diagnostic::error(ErrorCode::E2041)
    .with_message("description of what went wrong")
    .with_label(span, "explanation of this location")

6. Verify

Run cargo t -p ori_diagnostic to check that all tests pass, including the exhaustive classification test (test_all_variants_classified) and the count test (test_all_is_complete). The classification test ensures every code is matched by exactly one is_* predicate; the count test catches unintentional additions or removals.

Import and Module Errors

Import and module resolution errors are reported as E2xxx type checker errors because they are detected during the type checking phase, not during a separate resolution pass. This design reflects Ori’s architecture: the parser produces an AST with unresolved use declarations, and the type checker resolves them as part of building the typed IR.

The most common import-related diagnostics are:

  • Module not found: reported through the diagnostic system when a use path does not resolve to any known module. The error includes the full path that was attempted and, when possible, suggests similarly-named modules.

  • Item not exported: reported as E2003 (unknown identifier) when the target module exists but does not export the requested name. The diagnostic context indicates that the name exists in the module but is private (not marked pub).

  • Circular imports: detected during module resolution when two modules depend on each other. The diagnostic traces the cycle to help the programmer understand the dependency chain and decide where to break it.

These are unified under the E2xxx range rather than given their own range because, from the programmer’s perspective, the symptoms are identical to other name-resolution failures: “I used a name and the compiler does not know what it refers to.” Splitting them into a separate range would add complexity without improving the programmer’s ability to fix the error.