Limits
Set resource budgets for sandbox execution and host function traffic.
Limits
Limits bound the time, memory, data, and host interaction available to one invocation. They protect the host from accidental overuse and from source that tries to consume unbounded resources.
Concept
Every invocation has a RunLimits budget. The budget applies to the complete
invocation, including guest execution, host function calls, result
serialization, and continuation operations.
The defaults are a starting point, not a policy. A short data transformation and a coding agent rarely want the same budget.
Defaults
| Limit | Default | Bounds |
|---|---|---|
timeoutMs | 30 seconds | The complete invocation |
memoryLimitBytes | 64 MiB | QuickJS memory |
maxStackSizeBytes | 2 MiB | QuickJS stack |
maxSourceBytes | 256 KiB | UTF-8 source |
maxResultBytes | 1 MiB | Serialized result data |
maxConsoleOutputBytes | 64 KiB | Guest console output |
maxHostFunctionArgumentsBytes | 1 MiB | Serialized arguments for one call |
maxHostFunctionOutputBytes | 4 MiB | Serialized output or interruption payload |
maxBridgeRequests | 256 | Host function calls in one invocation |
maxInFlightBridgeRequests | 32 | Host function calls active at once |
maxContinuationBytes | 32 MiB | Serialized continuation state |
Every configured value must be a positive integer.
Implementation
Use createRunner() when several invocations should share the same policy:
import { createRunner } from 'run';
const MiB = 1024 * 1024;
const runner = createRunner({
limits: {
timeoutMs: 15_000,
memoryLimitBytes: 32 * MiB,
maxStackSizeBytes: 1024 * 1024,
maxSourceBytes: 128 * 1024,
maxResultBytes: 512 * 1024,
maxConsoleOutputBytes: 32 * 1024,
maxHostFunctionArgumentsBytes: 256 * 1024,
maxHostFunctionOutputBytes: MiB,
maxBridgeRequests: 100,
maxInFlightBridgeRequests: 8,
maxContinuationBytes: 8 * MiB,
},
});Every runner.run() call inherits these values:
const result = await runner.run({
source,
hostFunctions,
});An individual invocation can override only the values that need to change:
const result = await runner.run({
source,
hostFunctions,
limits: {
timeoutMs: 5_000,
maxResultBytes: 128 * 1024,
},
});The five-second timeout and 128 KiB result budget replace the runner defaults for this invocation. Every other limit continues to come from the runner.
The convenience run() function also accepts per-invocation limits:
import { run } from 'run';
const result = await run({
source,
hostFunctions,
limits: {
timeoutMs: 5_000,
memoryLimitBytes: 32 * 1024 * 1024,
},
});Selection
Start with the defaults and measure representative workloads before changing them. Lower a limit when the workload has a predictable shape, and raise it only when the application is prepared to absorb the additional resource use.
Source, argument, output, result, and continuation limits are measured after their relevant UTF-8 encoding or serialization step. A JavaScript object that appears small can have a much larger serialized representation.
Memory and worker concurrency should be planned together. Raising
memoryLimitBytes increases the maximum memory available to every active
worker, which may reduce the number of invocations a process can safely run at
once.
maxBridgeRequests controls total host interaction, while
maxInFlightBridgeRequests controls parallel host interaction. Lowering the
in-flight limit can protect a database or external service even when the total
number of host function calls remains unchanged.
Failures
Limit violations reject the invocation. The most relevant stable codes are:
RUN_TIMEOUTindicates that the invocation exceededtimeoutMs.RUN_SOURCE_TOO_LARGEindicates that the source exceededmaxSourceBytes.RUN_BRIDGE_LIMITindicates that total or in-flight host function traffic exceeded its configured limit.RUN_HOST_FUNCTION_ERRORcan indicate that host function arguments exceeded the host boundary or that a host function request was otherwise invalid.RUN_SERIALIZATION_ERRORindicates that a transferred value was not serializable or exceeded another serialization boundary.RUN_PROTOCOL_ERRORcan indicate that continuation state or a continuation token exceededmaxContinuationBytes.
QuickJS memory and stack exhaustion do not have dedicated stable error codes. Treat their failures as runtime failures, monitor them, and adjust the budget only after confirming that the source is behaving as expected.
Use RunError.isInstance() and inspect error.code when the application needs
different responses:
import { RunError } from 'run';
try {
await runner.run({ source, hostFunctions });
} catch (error) {
if (RunError.isInstance(error)) {
console.error('Run failed', {
code: error.code,
details: error.details,
});
}
throw error;
}Do not automatically retry a limit violation with a larger budget. First determine whether the source is behaving as expected and whether host side effects are safe to repeat.