The Casa compiler provides Rust-style error diagnostics with source context. When errors are detected, the compiler shows the error kind, a description, the file location, and the relevant source line with carets highlighting the problematic span.
error[UNDEFINED_NAME]: Identifier `foo` is not defined
--> examples/fizzbuzz.casa:15:5
|
15 | foo
| ^^^
Each error includes:
- Error kind in brackets (e.g.
UNDEFINED_NAME) - Message describing the problem
- File path, line, and column where the error occurs
- Source line with carets (
^^^) underlining the relevant span
Some errors include additional context:
- Expected and Got (or Inferred) lines showing what was expected versus what was found
- Notes with secondary source annotations pointing to related locations (e.g. where a mismatched value was originally pushed, or which branches have incompatible stack effects)
When multiple errors are found, they are all printed before the compiler stops, followed by a summary line:
Found 3 error(s).
Malformed tokens or structural problems in the source code.
"unterminated string
error[SYNTAX]: Unclosed string literal
Invalid escape sequences in strings are also reported as SYNTAX errors:
"bad\q"
error[SYNTAX]: Invalid escape sequence `\q`
The parser expected one token but found another.
struct Foo { x int }
error[UNEXPECTED_TOKEN]: Unexpected token
Expected: `:`
Got: `int`
An identifier is used but not defined as a function, variable, struct, or intrinsic.
foo bar baz
error[UNDEFINED_NAME]: Identifier `foo` is not defined
error[UNDEFINED_NAME]: Identifier `bar` is not defined
error[UNDEFINED_NAME]: Identifier `baz` is not defined
Found 3 error(s).
Multiple undefined names are collected and reported together.
An identifier is defined more than once in a context where duplicates are not allowed.
fn foo { }
fn foo { }
error[DUPLICATE_NAME]: Identifier `foo` is already defined
A construct appears in a scope where it is not allowed (e.g. defining a function inside another function).
fn outer {
fn inner { }
}
error[INVALID_SCOPE]: Functions should be defined in the global scope
This also applies to impl blocks and struct definitions:
fn outer {
impl Foo { }
}
error[INVALID_SCOPE]: Implementation blocks should be defined in the global scope
An operation tried to consume a value (or values) from the stack but the stack was empty.
fn foo { print rot }
error[STACK_UNDERFLOW]: Stack underflow: expected value with trait `Display` but stack is empty
error[STACK_UNDERFLOW]: Stack underflow: expected argument 1 of `rot` but stack is empty
The expectation phrase identifies what the operation needed:
value with traitT`` — operations that dispatch on a trait (e.g.print, `==`, `<`)argument N ofop`` — stack intrinsics that consume more than one value (swap, `over`, `rot`); `N` counts from the top of the stack (1 = topmost)value forop`` — single-value stack intrinsics (drop, `dup`)parameter N ofname`` — function call missing argumentsvalue— generic fallback when no operation-specific context is available
When a slot cannot be filled, downstream operations may show <missing> for the absent value. See Cascade Errors.
A type does not match what was expected.
fn bad[T] T T -> T T { }
42 "hi" bad
error[TYPE_MISMATCH]: Type variable `T` bound to `str` but got `int`
If got shows <missing>, an earlier error left the slot without a real type — fix the earlier error first. See Cascade Errors.
Branches of a conditional or loop leave the stack in inconsistent states. The error shows each branch's stack effect so you can see which branch diverges.
fn branchy bool -> int {
if dup then
1 2
else
3
fi
}
error[STACK_MISMATCH]: Branches have incompatible stack effects
--> examples/multi_error.casa:27:5
|
27 | fi
| ^^
Note: `if` branch has signature `? -> ? int int`
--> examples/multi_error.casa:23:5
|
23 | if dup then
| ^^
Note: `else` branch has signature `? -> ? int`
--> examples/multi_error.casa:25:5
|
25 | else
| ^^^^
A function's declared stack effect does not match the inferred stack effect from its body.
fn bad a:int -> str { a 1 + }
bad
error[SIGNATURE_MISMATCH]: Invalid signature for function `bad`
Expected: int -> str
Inferred: ? -> int
Attempting to assign a value of a different type to an existing variable.
42 = x
"hello" = x
error[INVALID_VARIABLE]: Cannot override global variable `x` of type `int` with other type `str`
A block construct is missing its closing keyword or has mismatched block markers.
if true then
42 print
error[UNMATCHED_BLOCK]: `if` without matching `fi`
When the compiler reports an error that consumes a value from the stack — most commonly STACK_UNDERFLOW — the affected slot is tagged with the placeholder type <missing> so type checking can keep going. If a later operation reads that slot, you may see <missing> in its diagnostic:
fn two_generics[T] a:T b:T { }
fn foo {
print # underflow: nothing to print
2 two_generics # this also fails because the underflow tainted the stack
}
error[STACK_UNDERFLOW]: Stack underflow: expected value with trait `Display` but stack is empty
error[TYPE_MISMATCH]: Type variable `T` bound to `int` but got `<missing>`
The second error is a cascade — its real cause is the underflow above. Fix the upstream error first; the cascade error usually disappears on its own. <missing> is distinct from ? (an unconstrained type that the compiler is still inferring).
The compiler collects as many errors as possible within each compilation phase before stopping. For example, the identifier resolution phase will report all undefined names at once rather than stopping at the first one.
Within a single function, type checking stops at the first error because the stack state becomes unreliable. However, when checking all functions, errors from different functions are collected and reported together.
A type is used where a trait bound is required, but it does not implement all required methods.
struct Foo { x: int }
Map::new (Map[Foo int]) = m # Foo has no hash or eq methods
error[MISSING_TRAIT_METHOD]: Type `Foo` does not satisfy trait `Hashable`
A type has a method with the right name for a trait, but its stack effect does not match the trait's requirement.
error[TRAIT_SIGNATURE_MISMATCH]: Method signature does not match trait requirement
The compiler also reports non-fatal warnings. Currently the only warning kind is:
A function parameter is declared but not used in the function body and is instead passed through the stack untouched.
warning[UNUSED_PARAMETER]: Unused parameter `int` in function `add`
- Language Server -- real-time diagnostics in your editor
- Types and Literals -- type system that generates these errors
- Traits -- trait-related error kinds