8.8 Limits inside the sandbox and diagnosing failures

A custom service runs under hard, host-enforced limits, and its failures surface as structured errors. Knowing the limits and the error vocabulary turns "it doesn't work" into a quick diagnosis.

The limits

Every invocation runs under (operator-configurable; defaults shown):

  • Wall clock — 30 s for a service. Exceeding it terminates the invocation with limit_exceeded (wall_clock_ms).
  • Memory — a heap cap kills an allocation bomb the same way (limit_exceeded memory_bytes), with no impact on the process or other tenants.
  • Outbound calls — budgeted (default 64 per invocation).
  • Body materialization — capped (default 100 MB).

Repeated breaches trip the tenant's circuit breaker (9.5), so pathological code can't keep re-occupying engine threads. Design handlers to finish well inside these bounds; treat termination as a possibility, not an impossibility.

The failure vocabulary

When a custom service misbehaves, the problem+json code tells you what happened:

Status / code Meaning First thing to check
502 contract_violation — "bundle error: …" The module failed to evaluate A missing global (check 8.3's supported list) or top-level I/O
502 contract_violation — "handle rejected: …" The handler threw; rejection text included Your handler logic; the included message
403 capability_denied (with a capability field) Used an ungranted/misnamed capability, or a non-allowlisted host The grant name matches ctx.request's; the host is in the allowlist (8.5)
503 limit_exceeded (wall_clock_ms/memory_bytes) Runaway loop or allocation; isolate terminated cleanly An infinite loop, unbounded data, a missing base case
501 engine_unavailable The server build lacks the engine for this bundle type, or no HTTP adapter is wired for httpOut The server was built with the right feature (js/wasm/http)
404 "deployed code … not found" The mount's code: ref isn't deployed to this tenant Deploy to the tenant; refs are per-tenant (8.7)

A diagnostic flow

  1. Read the code and detail in the problem+json body — they're specific (the JS exception text is included for 502s).
  2. Grab the X-Trace-Id and look up that trace in the logs (Part 10) — your console.log/ctx.log lines are stamped with the same trace, so they line up with the failing request.
  3. For capability_denied, check the capability field against your grants keys and the host allowlist — name mismatches are the most common cause.
  4. For limit_exceeded, look for unbounded loops or data; remember timers are virtual (a "sleep" won't burn wall time, but a busy loop will).

Logs are your friend

Because sandbox console.log/ctx.log output is captured into the tenant's structured logs with the invocation's trace id, the log reader (Part 10) is the primary debugging tool for deployed code — much like a browser console, but server-side and correlated to each request.

That completes custom services. Part 9 zooms back out to the cross-cutting HTTP behavior — discovery, errors, idempotency, caching, and limits — that applies to every service, custom or prebuilt.


Previous: 8.7 Versioning, rollback, mounting · Manual home · Next: 8.9 Loadable storage adapters →