Runner lease failures
A retained sandbox may still exist after the worker driving its turn loses its lease. Start with the attempt records and runner logs: container presence alone does not prove that the native session can safely continue.
Lease timing and retries
The lease and execution timeout govern different things:
Setting |
Default |
Purpose |
|---|---|---|
Attempt lease |
120 seconds |
Fences the worker’s authority to write. |
Heartbeat interval |
30 seconds |
Renews that authority on a separate thread. |
Heartbeat HTTP timeout |
10 seconds |
Limits each renewal request. |
Retry delay after heartbeat failure |
5 seconds |
Retries while the current lease remains valid. |
Agent execution timeout |
3,600 seconds |
Limits the turn itself. |
Ordinary control RPC timeout |
30 seconds |
Default for other control requests. |
A successful heartbeat restores the normal interval. HTTP 409 rejects the lease permanently: renewal and execution stop, and retries cannot restore the expired owner. Renewal does not depend on model output, tool activity or progress events, so a quiet call can span many lease periods.
The runner reaches the same verdict without a 409 when the lease cannot still
be valid: once its last acknowledged renewal is a whole lease old, or when the
control plane refuses the worker credential itself (HTTP 401, 403 or 404,
which no retry changes). It then stops the agent as for a 409 instead of
letting it write to the shared workspace after the reaper has superseded the
attempt. A lease must therefore outlast one heartbeat interval plus one
request timeout (40 seconds): a shorter AGENT_LEASE_TTL is ignored in favour
of the default, and a shorter --lease-ttl is refused. A cancellation does not
end renewal; the lease is kept while the runner releases or drains the turn.
With the first heartbeat at 30 seconds and repeated ten-second read timeouts, retry requests start at 30, 45, 60, 75, 90 and 105 seconds after claim. This illustrates opportunities to reconnect before the original 120-second lease ends; it is not a wall-clock guarantee. HTTP timeout limits apply separately to connection, pool, write and read operations. An outage longer than the remaining lease still loses the lease.
The regression suite drives a silent six-minute turn through the real heartbeat thread, stream loop and HTTP client against a simulated lease endpoint. It covers healthy renewal and timed-out reads followed by recovery at 100 or 105 seconds. The previous cadence loses the latter case. These tests reproduce a failure mechanism; they do not establish the cause of a historical incident.
Diagnose from positive evidence
Distinguish a renewal-request timeout from an execution timeout, explicit
cancellation or agent exit. lease_expired proves only that the database did
not persist a timely renewal. Missing error events do not prove the worker was
healthy: the control outage that blocks renewal can also block error reporting.
Use these ordinary process-log records:
Record |
Evidence |
|---|---|
|
Job, attempt, worker and host identifiers. |
|
Failed operation/stage, job ID, exception class and HTTP status when available. |
|
Consecutive failures, time since the last acknowledgement (or heartbeat start), and next retry delay. |
|
Renewal succeeded after failures. |
The failure diagnostics exclude request URLs, tokens, response bodies and exception messages. The heartbeat’s exception class/status are logged once per outage; retry records carry the changing count and timing.
Correlate logs with the attempt’s persisted lease_owner, lease_expires_at
and event timestamps. Collect runner and control-plane logs outside their
containers: replacing a container can delete its Docker log history. The runner
does not configure an external collector or recover deleted logs.
An attempt_superseded event does not establish that another attempt ran.
Retained sessions can become lost without a replacement, including when
activation ownership is uncertain during preparation. The UI announces another
attempt only when its events exist.
Reporting and acknowledgement failures
Error-event upload and the terminal finish request are independent. The
runner attempts the terminal transition even if event upload fails. If both
remain unavailable, the reaper is the final backstop.
Post-claim preparation outside the shared execution handler, such as image inspection or manifest validation, has an outer failure handler too. It attempts a terminal failure before stopping heartbeat renewal and logs the exception class even if reporting fails. An explicit lease rejection skips that write. This records early setup failures without repairing their cause.
After supervisor and control-plane settlement both succeed, a failed upload of
the final shared_workspace_settled notice does not fail the turn: the runner
still submits its successful terminal state. Lease rejection prevents that
write. Earlier settlement failures remain failures.
Admission and settlement receipts allow up to three attempts, using ten-second HTTP timeouts and five-second delays. Only transport failures and HTTP 408/502/503/504 are retried; 409/410 are definitive. The prompt is sent exactly once, after its admission acknowledgement succeeds.
Supervisor settlement retains the last token’s receipt until another activation takes ownership. Control-plane settlement returns the recorded boundary only for the same still-live attempt and activation. Repeating it does not increment the workspace epoch, append a snapshot or change the native turn ordinal. A sandbox manager restart loses its in-memory receipt, so uncertain completion across a restart fails closed.
When a retained conversation can continue
A reported preparation failure before admission can leave an existing conversation usable. The runner abandons the activation and reports a failed turn; the next message can resume the retained native session. This requires a known retained container. Failure before its identity was recorded does not provide that proof, and error-event availability must not gate the report.
An admission request with no received acknowledgement is different: the
database may have committed it even though the runner has not sent the prompt.
The membership then records the job as admitted. abandon only releases
pre-admission turns (admitted_job_id IS NULL), so it cannot release this
ambiguous turn; the terminal transition marks its context LOST. The runner never
replays an unconfirmed admitted prompt.
Recovery after an expired activation or killed runner requires a further
protocol: durably quarantine the member, fence the old runner’s preparation
commands and sandbox manager token, verify the exact retained container and native turn
boundary, then resume that session or report it unrecoverable. A sandbox manager
busy=false observation is insufficient because workspace setup commands run
outside its agent-turn transport. Never requeue a turn solely because its
admission field is empty, clear a lost flag, or replay an admitted prompt to
repair a display state.
Owner-facing wording describes interrupted responses and unavailable conversations in plain language. Infrastructure details stay in API records and operator logs; task errors and assistant messages remain intact. Historical LOST memberships remain disabled after wording changes.
The safe release case: a child never accepted its turn
The host runs each turn in a child process. It can release a claim back to the queue only when it can prove that child never accepted the turn:
Process creation failed.
Payload delivery failed.
The child closed its handover descriptor without accepting, including a failed read-only Docker/network preflight before acceptance.
A silent child that remains alive is not proof and is never released. After acceptance, crashes, kills and lost acknowledgements belong to the lease protocol; the conversation may become lost instead of repeating the turn.
Release uses POST /v1/agent/worker/jobs/<job>/release with that turn’s worker
token. The control plane requires the same still-running attempt and lease
generation and no membership that admitted the job. The database statement
enforces the admission condition, so an incorrect host observation cannot
requeue admitted work.
Consecutive launch failures stop claims on that host and back off. Heartbeats
report launch_error, which appears beside the host on Admin → Servers.