Skip to main content
This page is the reference for the 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 in jobs/{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 to failed, 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 to polling.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 to session_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 uses user_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:
The running worker observes the deletion event and stops that section with SESSION_DETACHED_EXTERNALLY. Other sections continue.

Same-user worker races

Two worker installs sharing the same API key (same user_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 local thread.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 a pending_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

For ask_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 into pending 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:
There is no inline editor in blobhub-web for these fields in v1; a dedicated editor is a deferred fast-follow.

See also