session_agent_harness job type: the error codes it can raise — in three
classes beyond the generic process codes that fire before any section runs — and the v1
limitations specific to driving agents over a session.
Section validation
These fire during preflight, before the section is contacted. They exit the process with the code, the same way the generic process codes do.
This static check can’t catch two sections naming the same session under different
session_id
spellings (one by uuid, one by alias) — that collision surfaces after resolution instead, as the
section-level DUPLICATE_SECTION_TARGET below.
Section-level errors
One section fails to attach to its session; the failure is persisted injobs/{job_id}/section.yaml.attachment.error and the section stops, while the worker keeps running all other
sections.
Thread-level errors
A single thread transitions tofailed, terminal until the user resets it (instance.state = "pending").
The error detail is persisted locally in jobs/.../threads/{alias}/thread.yaml (agent.error) — it is
not written to the thread envelope. It surfaces to other clients as a thread_failed item in the worker
activity thread.
Transient errors
Logged and retried with exponential backoff up topolling.backoff_max_ms. They surface in the TUI warning
ribbon and do not trigger any state-machine transition on their own (a long-running transient may
eventually present as a thread-level THREAD_POST_FAILED after exhaustion).
Limitations
These trade-offs are specific tosession_agent_harness. For generic worker limitations (no hot config
reload, single-process / single-machine), see Reference.
guarded is a guardrail, not a sandbox
A policy (permissions: guarded) catches string/glob-matched mistakes; it is not OS-level confinement,
and of the two supported harnesses only codex has a sandbox underneath it — claude_code has none, so
there the policy is the only gate at all. See
Job Session Object → Policy and guarded permissions for the
full statement, the per-harness capability table, what codex’s sandbox refuses and escalates for a
write + outside: work_folder rule (including disguised escapes like hard links, the
.git/.agents/.codex carve-outs, and the no-sandbox-platform case), and why an exec rule governs
which commands run — never where they write — on either harness. Read it before writing rules for a
sensitive workspace, not
after discovering a gap the hard way.
Same-user posting collision
A human posting into a thread via the BlobHub web UI as the same user whose API key the worker uses is classified by the worker as “self” and ignored. The worker usesuser_id as the only signal for filtering its
own emissions out of the inbound stream; there is no per-post “posted by worker” flag in v1. Run the worker
under a dedicated service-account user with its own API key so humans posting as themselves are seen as
not-self and reach the agent.
No detach command
There is no blobhub-worker detach subcommand in v1. To free a session manually, delete the worker session
object directly:
SESSION_DETACHED_EXTERNALLY.
Other sections continue.
Same-user worker races
Two worker installs sharing the same API key (sameuser_id) will race to overwrite each other’s worker
marker silently — both will run and both will post. One worker install = one service-account user with one API
key; don’t share credentials across machines.
No agent retry on failure
When an agent crashes or fails, the worker records the error in the localthread.yaml, transitions the
thread to failed, and posts a thread_failed activity item. It does not auto-retry. Inspect thread.log
/ the local agent.error.message, then update the envelope to set instance.state = "pending"; the worker
observes the change and re-runs the activation.
Unresolved interactive prompts don’t survive restart
If the worker is restarted while apending_prompt is unanswered, the prompt is cancelled with
pending_prompt_resolved (reason: worker_restart). The agent re-asks on resume if it still needs the input.
Multi-question prompt answering is best-effort
Forask_user_question prompts with multiple questions, the plain-text answer parser is best-effort
(line-prefix 1) ..., 2) ... format). Unparseable replies fall back to each question receiving the raw text
as a free-text answer. Answer one question at a time when the agent asks several at once.
No native UI for thread metadata editing
To put a thread intopending state for the worker to pick up, you currently update the thread envelope’s
value.thread.metadata directly via upload_session_object (REST or SDK) — for example:

