Resource Limits
Risor provides three mechanisms to limit script execution: instruction count limits, call stack depth limits, and timeouts. Use them when running untrusted or user-provided scripts.
Instruction Limit
Use WithMaxSteps to cap the total number of bytecode instructions the VM
will execute. This prevents infinite recursion and runaway computations.
result, err := risor.Eval(ctx, source, risor.WithMaxSteps(10000))When the limit is exceeded, the error wraps risor.ErrStepLimitExceeded:
result, err := risor.Eval(ctx, source, risor.WithMaxSteps(10000))
if errors.Is(err, risor.ErrStepLimitExceeded) {
log.Println("script exceeded instruction limit")
}A value of 0 means unlimited (the default).
Stack Depth Limit
Use WithMaxStackDepth to limit how deeply functions can nest. This catches
infinite recursion that might otherwise exhaust memory.
result, err := risor.Eval(ctx, source, risor.WithMaxStackDepth(100))When the limit is exceeded, the error wraps risor.ErrStackOverflow:
result, err := risor.Eval(ctx, source, risor.WithMaxStackDepth(100))
if errors.Is(err, risor.ErrStackOverflow) {
log.Println("script exceeded stack depth limit")
}Timeout
Use WithTimeout to set a wall-clock deadline for script execution. This is
a hard limit. When the timeout expires, execution is cancelled via the
Go context.
result, err := risor.Eval(ctx, source,
risor.WithTimeout(100 * time.Millisecond),
)When the timeout is exceeded, the error is context.DeadlineExceeded:
result, err := risor.Eval(ctx, source,
risor.WithTimeout(100 * time.Millisecond),
)
if errors.Is(err, context.DeadlineExceeded) {
log.Println("script timed out")
}Combining Limits
All three limits can be used together. The first limit reached will terminate execution.
result, err := risor.Eval(ctx, source,
risor.WithEnv(risor.Builtins()),
risor.WithMaxSteps(50000),
risor.WithMaxStackDepth(50),
risor.WithTimeout(5 * time.Second),
)Error Handling
Each limit produces a distinct error type, making it straightforward to identify which limit was hit:
| Limit | Error |
|---|---|
| Instruction count | risor.ErrStepLimitExceeded |
| Stack depth | risor.ErrStackOverflow |
| Timeout | context.DeadlineExceeded |
Use errors.Is to check for these errors:
switch {
case errors.Is(err, risor.ErrStepLimitExceeded):
// too many instructions
case errors.Is(err, risor.ErrStackOverflow):
// recursion too deep
case errors.Is(err, context.DeadlineExceeded):
// wall-clock timeout
}Recommendations
- Untrusted scripts. Always set all three limits. A step limit of 100,000 and a timeout of 5 seconds are reasonable starting points.
- Internal scripts. You may only need a timeout to guard against bugs.
- Expression evaluation. If you use
WithSyntax(risor.ExpressionOnly), a modest step limit (e.g. 10,000) is usually sufficient since expressions cannot define recursive functions.