ESC
Type to search...
S
Soli Docs

Error Handling

Try/catch/finally for exception handling, throw for raising errors.

Try / Catch / Finally

try / catch / finally

Handles exceptions with try/catch blocks, using end-delimited syntax just like if, while, and for.

# Basic try/catch
try
    let result = 10 / 0
catch e
    print("Error: " + str(e))
end

# With finally (always runs)
try
    let data = read_file("config.sl")
    print(data)
catch e
    print("Failed: " + str(e))
finally
    print("Cleanup done")
end

# Try/finally without catch
try
    process_data()
finally
    close_connection()
end

Catch Variable Syntax

The catch variable can be written with or without parentheses:

# Without parentheses
try
    risky()
catch e
    print(e)
end

# With parentheses
try
    risky()
catch (e)
    print(e)
end

Throwing Exceptions

throw

Raises an exception that can be caught by a surrounding try/catch block.

def divide(a: Int, b: Int) -> Int
    if b == 0
        throw "Division by zero"
    end
    a / b
end

try
    let result = divide(10, 0)
catch e
    print("Caught: " + str(e))  # "Caught: Division by zero"
end

End Syntax

Try/catch supports end-delimited blocks:

try
    let result = risky_operation()
catch e
    print("Error: " + str(e))
finally
    cleanup()
end

Typed Catch

catch ClassName e

Catch specific error types by class name. Multiple catch blocks are tried in order. A bare catch without a type acts as a catch-all.

# Define error classes
class NotFoundError
    message: String
    new(msg: String)
        this.message = msg
    end
end

class ValidationError
    message: String
    new(msg: String)
        this.message = msg
    end
end

# Multiple typed catch blocks
try
    throw new NotFoundError("User not found")
catch NotFoundError e
    print("404: " + e.message)
catch ValidationError e
    print("Invalid: " + e.message)
catch e
    print("Unknown error: " + str(e))
end
Catching by Superclass

Typed catches walk the inheritance chain. A catch BaseError will also catch any subclass of BaseError.

class AppError
    message: String
    new(msg: String)
        this.message = msg
    end
end

class NotFoundError extends AppError
    new(msg: String)
        super(msg)
    end
end

class PermissionError extends AppError
    new(msg: String)
        super(msg)
    end
end

# Catches NotFoundError because it extends AppError
try
    throw new NotFoundError("Page not found")
catch AppError e
    print("App error: " + e.message)
end
Unmatched Exceptions

If no typed catch matches, the exception is re-thrown to the next outer try/catch. Add a bare catch at the end to handle all remaining exceptions.

class ErrorA {}
class ErrorB {}

try
    try
        throw new ErrorB()
    catch ErrorA e
        # Does NOT match ErrorB
        print("A")
    end
catch e
    # ErrorB bubbles up here
    print("Caught in outer: " + str(e))
end

Typed Catch Rules

  • Typed catches only match class instances (strings, ints, etc. will not match)
  • Catches are tried in order — put more specific types first
  • A bare catch e catches everything (including non-instance values)
  • Subclasses match their parent class catches (walks the inheritance chain)
  • Works with both end and {} syntax

Nested Try/Catch

Try/catch blocks can be nested for fine-grained error handling:

try
    print("Outer try")
    try
        throw "inner error"
    catch e
        print("Inner catch: " + str(e))
    end
    print("After inner try")
catch e
    print("Outer catch: " + str(e))
end