ReferenceLimits and Errors

Sandbox limits

Two profiles via VM::with_limits, same compiler.wasm runs unsandboxed in trusted contexts, clamped in untrusted.

Limitnone() (default)sandbox()What hitting it raises
Max call depth1,000256RecursionError
Max operationsunbounded100,000,000RuntimeError
Max heap bytes10,000,000100,000MemoryError

Integer width

Two-tier:

  • Inline (fast): 47-bit signed in a NaN-boxed Val. Range +/-2^47 (+/-140_737_488_355_327). One ALU op per arithmetic, no allocation.
  • Wide (slow): i128 in HeapObj::LongInt. Range +/-2^127 - 1. Auto-used when a literal exceeds 47-bit or inline arithmetic overflows.

Outside +/-2^127 raises OverflowError. Promotion is automatic, user code doesn’t see the boundary.

print(140737488355327) # inline, fast path
print(2 ** 47) # 140737488355328: auto-promotes to LongInt
print(2 ** 100) # 1267650600228229401496703205376
try:
    print(2 ** 127) # past the i128 cap
except OverflowError:
    print("overflow")
140737488355327
140737488355328
1267650600228229401496703205376
overflow

Caveats

  • pow(a, b, m) modular: modulus must be < 2^63 (larger overflows i128 in the multiply). Hard cap without arbitrary-precision arithmetic.
  • No CPython-style unbounded ints: by design, edge workloads don’t need wider than 128 bits; crypto-scale math is out of scope.
  • Float vs LongInt mixing: == works (LongInt -> f64), but dict/set hashing follows raw Val bits, {long_int: x} indexed by a same-magnitude float misses. Coerce explicitly.

Triggering limits

# Recursion depth
def loop(n):
    return loop(n + 1)
 
try:
    loop(0)
except RecursionError:
    print("hit max depth")
hit max depth
# Heap quota, a tight loop allocating new objects
try:
    xs = []
    while True:
        xs = xs + [0] * 1000
except MemoryError:
    print("hit heap limit")

Source size

Source must be under 10 MiB; larger rejected at lex time.

Token limits

LimitValue
Max indent depth100
Max f-string depth200
Max expression depth200
Max instructions per chunk65,535

Prevent asymmetric DoS, small input producing an exponentially large parse tree.

Error types

Compile-time

Reported as Diagnostic { start, end, msg }, byte offsets into source; line/column computed lazily by render(). Caught before any code runs.

DiagnosticCause
expected X, got 'Y'Unexpected token
'(' was never closed (or '[' / '{')Bracket opened with no matching closer
')' does not match '[', expected ']'Wrong closer kind for innermost opener
unexpected ')', no matching openerCloser with no opener on the stack
unexpected ':' (missing 'if', 'while', 'for', ...)expr: at statement level
unterminated string literalString missing closing quote
unterminated triple-quoted string literalTriple-quoted string hit EOF
f-string was never closedF-string body hit EOF before close
inconsistent indentation: mixing tabs and spacesIndent mixes both whitespace kinds
'break' outside loopMisplaced control keyword
'continue' outside loopMisplaced control keyword
default 'except:' must be lastBare except not at end
expression too deeply nestedPast MAX_EXPR_DEPTH
program too large: exceeded maximum instruction limitPast MAX_INSTRUCTIONS

Runtime

Raised as VmErr; most catchable with try / except.

VariantClass nameWhen
TypeTypeErrorWrong operand type
TypeMsgTypeErrorWrong operand type (with context)
ValueValueErrorRight type, invalid value
AttributeAttributeErrorAttribute not found on object
NameNameErrorUndefined name
ZeroDivZeroDivisionErrorDivision or modulo by zero
OverflowOverflowErrorInteger arithmetic past +/-2^47
Raised("KeyError")KeyErrorDict / set lookup miss
Raised("IndexError")IndexErrorSequence index out of range
Raised("StopIteration")StopIterationIterator exhausted
Raised("AssertionError")AssertionErrorFailed assert
Raised("TimeoutError")TimeoutErrorwith_timeout deadline expired
Raised("CancelledError")CancelledErrorUser-thrown cancellation
Raised("SystemExit")SystemExitraise SystemExit(code); uncaught = clean host exit with that code
CallDepthRecursionErrorPast max_calls
HeapMemoryErrorPast heap limit
BudgetRuntimeErrorPast op limit
RuntimeRuntimeErrorInternal invariant or unsupported
Raised(custom)User raise X (X may be a class or string)

Exception hierarchy

Flat tree rooted at BaseException -> Exception. except walks parent links, except Exception catches RuntimeError, ValueError, KeyError, AssertionError, etc.; except RuntimeError catches RecursionError, NotImplementedError. SystemExit sits directly under BaseException, so except Exception does not catch it (use except SystemExit or a bare except).

try:
    raise RuntimeError("oops")
except Exception as e:
    print("caught via parent:", e)
 
try:
    [][0]
except Exception:
    print("caught IndexError as Exception")
caught via parent: oops
caught IndexError as Exception

User-defined classes don’t auto-extend the built-in BaseException tree but support single-level inheritance among themselves, except UserBase catches a raised UserSub when UserSub inherits from UserBase. raise X from Y raises X; the cause is discarded (no __cause__ / __context__ chaining).

Exception arguments

Caught exceptions expose constructor args as e.args (tuple). raise X("msg") and raise X(a, b) carry through; runtime-raised errors carry their message as a single arg; bare raise X produces an empty tuple.

try:
    raise TypeError("bad input")
except TypeError as e:
    print(e.args)
 
try:
    1 / 0
except ZeroDivisionError as e:
    print(e.args)
 
try:
    raise ValueError
except ValueError as e:
    print(e.args)
('bad input',)
('division by zero',)
()

Catching errors

def safe(f, x):
    try:
        return f(x)
    except TypeError:
        return "type"
    except ValueError:
        return "value"
    except ZeroDivisionError:
        return "zero"
    except:
        return "other"
 
print(safe(lambda x: 1 / x, 0))
print(safe(lambda x: int(x), "abc"))
print(safe(lambda x: len(x), 42))
zero
value
type

Environmental errors

Failures surfaced before the source reaches the compiler, no line/column preview, no parsed code to anchor to. Emitted as plain text, uncatchable from Python.

ErrorWhenResolution
input rejected: invalid utf-8 at byte NHost input bytes not valid UTF-8Re-encode as UTF-8
source file exceeds maximum size (10 MiB)Source over the 10 MiB lex-time capSplit or trim the input

Handle at the embedder layer (path validation, encoding, size check) before invoking the compiler.

Unsupported features at runtime

Parse but raise RuntimeError when executed:

try:
    import os
except RuntimeError as e:
    print("import:", e)

Exist for syntactic compatibility. For code reuse, use higher-order functions.

Determinism

Same source + input -> same output across runs and architectures (x86_64, aarch64, wasm32). No time, randomness, threading, or OS interaction. Heap-pool slot reuse is the only nondeterminism, observable through id(x) only, never ==, repr, or any other operation.