SnakeBridge provides a structured error system that translates Python exceptions into typed Elixir errors, enabling pattern matching and actionable suggestions.
Error Structure Types
ShapeMismatchError
For tensor/array shape incompatibilities:
%SnakeBridge.Error.ShapeMismatchError{
operation: :matmul, # :matmul, :broadcast, :elementwise, :index
shape_a: [3, 4],
shape_b: [5, 6],
message: "shapes cannot be multiplied (3x4 and 5x6)",
suggestion: "For matrix multiplication, A columns (4) must equal B rows (5)...",
python_traceback: "..."
}OutOfMemoryError
For GPU/CPU memory exhaustion:
%SnakeBridge.Error.OutOfMemoryError{
device: {:cuda, 0}, # {:cuda, N}, :mps, :cpu
requested_mb: 8192,
available_mb: 2048,
total_mb: 24576,
message: "CUDA out of memory",
suggestions: ["Reduce batch size", "Use gradient checkpointing", ...],
python_traceback: "..."
}DtypeMismatchError
For tensor dtype incompatibilities:
%SnakeBridge.Error.DtypeMismatchError{
expected: :float32,
got: :float64,
operation: :matmul,
message: "expected scalar type Float but found Double",
suggestion: "Convert with tensor.to(torch.float32)",
python_traceback: "..."
}Reference Errors
For ref lifecycle issues:
# Ref no longer exists
%SnakeBridge.RefNotFoundError{ref_id: "abc123", session_id: "session_456", message: "..."}
# Ref used in wrong session
%SnakeBridge.SessionMismatchError{
ref_id: "abc123", expected_session: "session_A", actual_session: "session_B"
}
# Malformed ref payload
%SnakeBridge.InvalidRefError{reason: :missing_id, message: "..."}Error Modes Configuration
Configure via application config:
config :snakebridge, error_mode: :raw # default:raw (Default)
Returns the original error payload:
{:error, %{message: "shapes cannot be multiplied", python_type: "RuntimeError", ...}}:translated
Applies ErrorTranslator to all errors:
case SnakeBridge.call("torch", "matmul", [a, b]) do
{:ok, result} -> result
{:error, %SnakeBridge.Error.ShapeMismatchError{} = e} ->
Logger.error("Shape error: #{e.suggestion}")
end:raise_translated
Raises translated errors for use with call!/4:
try do
SnakeBridge.call!("torch", "matmul", [a, b])
rescue
e in SnakeBridge.Error.ShapeMismatchError -> IO.puts("Shape: #{e.suggestion}")
e in SnakeBridge.Error.OutOfMemoryError -> IO.puts("OOM on #{inspect(e.device)}")
endErrorTranslator
The SnakeBridge.ErrorTranslator module detects patterns and converts errors.
Detection Patterns
Shape errors: shapes cannot be multiplied, size of tensor, incompatible shapes,
Dimension out of range, shape mismatch
OOM errors: out of memory, OutOfMemory, OOM (detects CUDA/MPS/CPU device)
Dtype errors: expected scalar type X but found Y, expected dtype torch.X but got torch.Y
Ref errors: Unknown SnakeBridge reference, SnakeBridge reference session mismatch,
Invalid SnakeBridge reference
Manual Translation
alias SnakeBridge.ErrorTranslator
error = %RuntimeError{message: "CUDA out of memory. Tried to allocate 8192 MiB"}
translated = ErrorTranslator.translate(error)
# => %SnakeBridge.Error.OutOfMemoryError{device: {:cuda, 0}, ...}
ErrorTranslator.dtype_from_string("Float") # => :float32
ErrorTranslator.dtype_from_string("torch.float64") # => :float64Error Flow
Python Exception
|
encode_error() + traceback.format_exc() [Python side]
|
JSON over gRPC to Elixir
|
SnakeBridge.Runtime receives {:error, payload}
|
apply_error_mode() --> :raw --> {:error, payload}
--> :translated --> {:error, structured_error}
--> :raise_translated --> raise structured_errorPython Error Encoding
Exceptions are encoded with type and traceback:
def encode_error(exception: Exception) -> dict:
return {
"success": False,
"error": str(exception),
"error_type": type(exception).__name__
}Tracebacks captured via traceback.format_exc().
Dynamic Exceptions
For Python types without specialized structs, SnakeBridge creates modules at runtime.
get_or_create_module
alias SnakeBridge.DynamicException
ValueError = DynamicException.get_or_create_module("ValueError")
# => SnakeBridge.DynamicException.ValueError
error = DynamicException.create("ValueError", "invalid literal for int()")
# => %SnakeBridge.DynamicException.ValueError{message: "...", python_class: "ValueError"}Rescuing Dynamic Exceptions
try do
SnakeBridge.call!("json", "loads", ["invalid"])
rescue
e in SnakeBridge.DynamicException.JSONDecodeError ->
IO.puts("JSON error: #{e.message}")
endFields: message, python_class, details, python_traceback
Best Practices
Choose Error Mode by Environment
- Development:
:translatedfor readable errors - Production with custom handling:
:raw - Production with exceptions:
:raise_translated
Pattern Match for Recovery
case SnakeBridge.call("torch", "matmul", [a, b]) do
{:ok, result} -> result
{:error, %ShapeMismatchError{}} ->
b_t = SnakeBridge.call!("torch", "transpose", [b, 0, 1])
SnakeBridge.call("torch", "matmul", [a, b_t])
{:error, %OutOfMemoryError{device: {:cuda, _}}} ->
SnakeBridge.call("torch", "matmul", [a, b], __runtime__: [device: :cpu])
endHandle Ref Lifecycle Errors
case SnakeBridge.method(ref, "predict", [input]) do
{:ok, result} -> result
{:error, %RefNotFoundError{}} ->
new_ref = SnakeBridge.call!("model", "load", [path])
SnakeBridge.method!(new_ref, "predict", [input])
{:error, %SessionMismatchError{}} ->
Logger.error("Cross-session ref usage")
{:error, :invalid_state}
endLog Tracebacks and Use Suggestions
Structured errors preserve the Python traceback (python_traceback field) and include
actionable suggestions (suggestion/suggestions fields) for user feedback.
See Also
- Refs and Sessions - Ref lifecycle
- Type System - Serialization details
- Best Practices - Patterns and recommendations