Walrus/Language

Error Handling

Understanding Walrus diagnostics: compile-time errors, intelligent suggestions, and runtime safety.

Updated April 17, 20266 min readWalrus / Errors / Diagnostics / Debugging

Walrus provides a robust diagnostic system designed to help you catch bugs early and fix them quickly. Errors in Walrus are split into two categories: Compile-time Diagnostics and Runtime Errors.


Compile-time Diagnostics

The Walrus compiler features a sophisticated diagnostic engine that provides clear, actionable feedback. When an error occurs, the compiler doesn't just stop; it often provides a snippet of your code with a marker and a helpful suggestion.

Source Snippets

Diagnostics include the filename, line number, and a visual marker pointing exactly to the problematic code.

TEXT
main.wlrs:5:12: error: use of uninitialized variable 'player'
  |
5 |     print(player.name)
  |           ^^^^^^

Intelligent Suggestions

The compiler includes a "Did you mean?" system that checks for typos in variable names, module members, and even compiler directives.

Walrus
let counter = 10
print(countr) # error: unknown identifier 'countr'; did you mean 'counter'?

Common suggestions include:

  • Identifier Typos: countr -> counter
  • Directive Typos: @stric -> @strict
  • Missing Operators: if x 10 -> did you mean a comparison like '<' or '=='?
  • Module Members: Math.sqroot -> Math.sqrt

Static Verification

Before your code runs, the Verifier performs a pass to ensure type safety and memory integrity.

Ownership Violations

Walrus will refuse to compile code that violates its memory safety rules. This prevents common bugs like "use-after-move" or conflicting borrows.

Walrus
let text = "walrus"
let moved = text
print(text) # error: use of moved value 'text'

Type Mismatches

Even though Walrus is dynamic in some areas, its static verifier catches obvious type mismatches:

  • Assigning an int to a variable expected to be an object.
  • Passing the wrong number of arguments to a function.
  • Returning a value that doesn't match the function's signature.

Runtime Errors

Some errors can only be caught while the script is running. When these occur, the VM throws a Runtime Error and stops execution safely.

Common runtime errors include:

  • Division by Zero: return 1 / 0
  • Invalid Indexing: Accessing an array with a bool or a string key that doesn't exist.
  • Native Handle Invalidation: Attempting to use a C++ object that the engine has already deleted.
  • Null Access: Accessing a property on a nil value.

Best Practices for Developers

  1. Read the Snippets: The markers (^^^^) are calculated accurately. They often point to the exact sub-expression causing the issue.
  2. Check Suggestions: The "Did you mean?" system uses Levenshtein distance to find the closest valid match. It's usually right!
  3. Strict Mode: Always use @strict at the top of your modules. This enables extra compiler checks that catch common scripting mistakes.
  4. Handle Native Safety: When working with engine objects, use .is_valid() checks if you suspect the host engine might have deleted an object since you last used its handle.