Error Handling
WasmtimeRuntime.jl provides comprehensive error handling for WebAssembly operations. This guide covers error types, handling patterns, and debugging strategies.
Error Types
WasmtimeError
The primary exception type for WebAssembly-related errors:
struct WasmtimeError <: Exception
message::String
end
All WebAssembly operations that can fail throw WasmtimeError
with descriptive messages.
Common Error Categories
Configuration Errors
try
config = Config()
config.ptr = C_NULL # Simulate invalid config
engine = Engine(config)
catch e::WasmtimeError
println("Configuration error: $(e.message)")
end
Module Errors
try
# Invalid WebAssembly bytes
invalid_bytes = UInt8[0x00, 0x00, 0x00, 0x00]
module_obj = WasmModule(engine, invalid_bytes)
catch e::WasmtimeError
println("Module compilation error: $(e.message)")
end
Instance Errors
try
# Invalid store
store.ptr = C_NULL
instance = Instance(store, module_obj)
catch e::WasmtimeError
println("Instantiation error: $(e.message)")
end
Function Call Errors
try
# Wrong parameter count or type
result = call(instance, "add", [42, 24, 33]) # Function expects 2 params
catch e::WasmtimeError
println("Function call error: $(e.message)")
end
Error Handling Patterns
Basic Try-Catch
function safe_wasm_operation()
try
engine = Engine()
store = Store(engine)
module_obj = WasmModule(engine, wasm_bytes)
instance = Instance(store, module_obj)
result = call(instance, "main", [])
return result
catch e::WasmtimeError
@error "WebAssembly operation failed" exception=e
return nothing
end
end
Specific Error Handling
function handle_specific_errors()
try
result = call(instance, "divide", [10, 0])
return result
catch e::WasmtimeError
if occursin("trap", lowercase(e.message))
@warn "Division by zero trapped"
return 0
elseif occursin("type", lowercase(e.message))
@error "Type mismatch in function call"
rethrow(e)
else
@error "Unknown WebAssembly error" exception=e
rethrow(e)
end
end
end
Error Recovery Patterns
function retry_with_fallback(primary_func, fallback_func, max_retries=3)
for attempt in 1:max_retries
try
return primary_func()
catch e::WasmtimeError
@warn "Attempt $attempt failed" exception=e
if attempt == max_retries
@info "Trying fallback function"
try
return fallback_func()
catch fallback_error::WasmtimeError
@error "Both primary and fallback failed"
primary=e fallback=fallback_error
rethrow(e) # Rethrow original error
end
end
sleep(0.1 * attempt) # Exponential backoff
end
end
end
Trap Handling
WebAssembly traps are runtime errors that occur during execution:
Common Trap Scenarios
# Division by zero
try
result = call(instance, "divide", [10, 0])
catch e::WasmtimeError
if occursin("trap", lowercase(e.message))
println("Trapped: Division by zero")
end
end
# Out of bounds memory access
try
result = call(instance, "read_memory", [1000000]) # Large offset
catch e::WasmtimeError
if occursin("trap", lowercase(e.message)) && occursin("bounds", lowercase(e.message))
println("Trapped: Memory access out of bounds")
end
end
# Stack overflow
try
result = call(instance, "recursive_function", [10000]) # Deep recursion
catch e::WasmtimeError
if occursin("trap", lowercase(e.message)) && occursin("stack", lowercase(e.message))
println("Trapped: Stack overflow")
end
end
Trap Recovery
function safe_wasm_call_with_trap_recovery(instance, func_name, params, default_value=nothing)
try
return call(instance, func_name, params)
catch e::WasmtimeError
if occursin("trap", lowercase(e.message))
@warn "Function trapped, returning default value"
function=func_name params=params exception=e
return default_value
else
# Re-throw non-trap errors
rethrow(e)
end
end
end
Resource Management Errors
Store Lifecycle Errors
function handle_store_lifecycle()
local store, instance
try
engine = Engine()
store = Store(engine)
module_obj = WasmModule(engine, wasm_bytes)
instance = Instance(store, module_obj)
# Use instance...
result = call(instance, "main", [])
catch e::WasmtimeError
@error "Store operation failed" exception=e
# Check if store is still valid
if isdefined(@__MODULE__, :store) && !isvalid(store)
@warn "Store became invalid during operation"
end
rethrow(e)
finally
# Cleanup is automatic via finalizers
# Manual cleanup if needed
end
end
Memory Management Errors
function handle_memory_errors(instance)
try
# Attempt to access memory export
memory_export = get_export(instance, "memory")
# Future: Memory operations
# data = read_memory(memory, offset, length)
catch e::WasmtimeError
if occursin("not found", e.message)
@warn "Memory export not found, module may not export memory"
return nothing
elseif occursin("bounds", e.message)
@error "Memory access out of bounds" exception=e
return nothing
else
rethrow(e)
end
end
end
Validation and Prevention
Input Validation
function validate_inputs(engine, wasm_bytes, func_name, params)
# Validate engine
if !isvalid(engine)
throw(ArgumentError("Invalid engine"))
end
# Validate WebAssembly bytes
if isempty(wasm_bytes)
throw(ArgumentError("Empty WebAssembly bytes"))
end
if !validate(engine, wasm_bytes)
throw(ArgumentError("Invalid WebAssembly module"))
end
# Validate function name
if isempty(func_name)
throw(ArgumentError("Empty function name"))
end
# Validate parameters
for (i, param) in enumerate(params)
if !is_wasm_convertible(typeof(param))
@warn "Parameter $i of type $(typeof(param)) may not be convertible to WebAssembly"
end
end
return true
end
Defensive Programming
function defensive_wasm_call(instance, func_name, params; timeout=5.0, max_fuel=10000)
# Input validation
if !isvalid(instance)
throw(ArgumentError("Invalid instance"))
end
if !isvalid(instance.store)
throw(ArgumentError("Invalid store"))
end
# Set up fuel limiting if available
try
add_fuel!(instance.store, max_fuel)
catch e::WasmtimeError
# Fuel not enabled, continue without it
@debug "Fuel consumption not enabled"
end
# Timeout handling (conceptual - actual implementation would need threading)
try
# Future: Implement timeout via epoch interruption
result = call(instance, func_name, params)
return result
catch e::WasmtimeError
if occursin("fuel", lowercase(e.message))
@error "Function execution exhausted fuel limit" limit=max_fuel
elseif occursin("epoch", lowercase(e.message))
@error "Function execution timed out" timeout=timeout
end
rethrow(e)
end
end
Error Reporting and Debugging
Detailed Error Context
function detailed_error_report(e::WasmtimeError, context...)
@error """
WebAssembly Error Details:
Message: $(e.message)
Context: $(join(string.(context), ", "))
Stack trace follows:
""" exception=(e, catch_backtrace())
end
# Usage
try
result = call(instance, "complex_function", [1, 2, 3])
catch e::WasmtimeError
detailed_error_report(e, "complex_function", "params=[1,2,3]", "instance=$(instance)")
rethrow(e)
end
Error Aggregation
struct WasmtimeErrorCollector
errors::Vector{Tuple{String, WasmtimeError}}
end
function collect_errors()
return WasmtimeErrorCollector([])
end
function try_operation!(collector::WasmtimeErrorCollector, operation_name::String, f)
try
return f()
catch e::WasmtimeError
push!(collector.errors, (operation_name, e))
return nothing
end
end
function report_collected_errors(collector::WasmtimeErrorCollector)
if !isempty(collector.errors)
@error "Multiple WebAssembly errors occurred:"
for (name, error) in collector.errors
@error " $name: $(error.message)"
end
end
end
# Usage
collector = collect_errors()
result1 = try_operation!(collector, "function1", () -> call(instance, "func1", []))
result2 = try_operation!(collector, "function2", () -> call(instance, "func2", []))
report_collected_errors(collector)
Error Context Stack
mutable struct ErrorContext
stack::Vector{String}
end
function push_context!(ctx::ErrorContext, description::String)
push!(ctx.stack, description)
end
function pop_context!(ctx::ErrorContext)
if !isempty(ctx.stack)
pop!(ctx.stack)
end
end
function with_error_context(f, ctx::ErrorContext, description::String)
push_context!(ctx, description)
try
return f()
catch e::WasmtimeError
error_msg = "$(e.message)\nContext: $(join(reverse(ctx.stack), " → "))"
rethrow(WasmtimeError(error_msg))
finally
pop_context!(ctx)
end
end
# Usage
ctx = ErrorContext([])
try
with_error_context(ctx, "Loading module") do
module_obj = WasmModule(engine, wasm_bytes)
with_error_context(ctx, "Creating instance") do
instance = Instance(store, module_obj)
with_error_context(ctx, "Calling main function") do
call(instance, "main", [])
end
end
end
catch e::WasmtimeError
println("Error with context: $(e.message)")
end
Best Practices
Error Handling Strategy
- Be Specific: Handle different error types appropriately
- Fail Fast: Validate inputs early to catch errors sooner
- Provide Context: Include relevant information in error messages
- Log Appropriately: Use different log levels for different error severities
- Clean Recovery: Ensure resources are cleaned up after errors
Error Prevention
# Comprehensive safety wrapper
function safe_wasm_execution(;
engine_config = nothing,
wasm_source,
function_name,
parameters = [],
fuel_limit = 10000,
validate_inputs = true
)
local engine, store, module_obj, instance
try
# Create engine with error handling
engine = if engine_config === nothing
Engine()
else
Engine(engine_config)
end
# Validate WebAssembly source
wasm_bytes = if isa(wasm_source, String)
if !isfile(wasm_source)
throw(ArgumentError("WebAssembly file not found: $wasm_source"))
end
read(wasm_source)
else
wasm_source
end
if validate_inputs && !validate(engine, wasm_bytes)
throw(ArgumentError("Invalid WebAssembly module"))
end
# Create store with fuel limiting
store = Store(engine)
try
add_fuel!(store, fuel_limit)
catch e::WasmtimeError
@debug "Fuel limiting not available"
end
# Create module and instance
module_obj = WasmModule(engine, wasm_bytes)
instance = Instance(store, module_obj)
# Execute function
result = call(instance, function_name, parameters)
return (result = result, success = true, error = nothing)
catch e::Exception
error_msg = if e isa WasmtimeError
"WebAssembly error: $(e.message)"
else
"System error: $(string(e))"
end
@error error_msg exception=e
return (result = nothing, success = false, error = e)
end
end
Testing Error Conditions
# Test error handling in your code
function test_error_handling()
# Test invalid engine
@test_throws WasmtimeError begin
config = Config()
config.ptr = C_NULL
Engine(config)
end
# Test invalid module
@test_throws WasmtimeError begin
WasmModule(engine, UInt8[0x00, 0x00, 0x00, 0x00])
end
# Test function call with wrong parameters
@test_throws WasmtimeError begin
call(instance, "add", []) # Missing parameters
end
end
Robust error handling is essential for production WebAssembly applications. By following these patterns, you can build resilient systems that gracefully handle errors and provide meaningful feedback to users and developers.