Documentation Index
Fetch the complete documentation index at: https://docs.blobhub.io/llms.txt
Use this file to discover all available pages before exploring further.
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.
| Code | Cause | Remediation |
|---|
MISSING_SESSION_KEYS | A section is missing one of org_id, blob_id, revision_id, session_id. | Complete the section’s session block. |
DUPLICATE_SECTION_TARGET | Two sections target the same session tuple. | Remove one of the duplicates. |
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.
| Code | Cause | Remediation |
|---|
SESSION_OWNED_BY_DIFFERENT_USER | The worker session object’s metadata.user.user_id differs from ours. | Manually free the session: delete_session_object(session_id, alias="worker"). |
SECTION_WORKER_OBJECT_MALFORMED | The worker session object exists but its value.type is not "thread". | Delete the malformed object and let the worker recreate it. |
SESSION_DETACHED_EXTERNALLY | The worker session object was deleted while the section was attached. | Re-attach by restarting the worker (or wait — the worker retries on next poll). |
SESSION_NOT_FOUND | The session 404’d or the API user lacks access. | Check the (org_id, blob_id, revision_id, session_id) tuple and permissions. |
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.
| Code | Cause | Remediation |
|---|
WORK_FOLDER_NOT_ABSOLUTE | workspace.work_folder is not an absolute path. | Update the envelope to use an absolute path. |
WORK_FOLDER_NOT_FOUND | The path doesn’t exist on the worker host. | Create the directory or fix the path. |
WORK_FOLDER_NOT_A_DIR | The path exists but isn’t a directory. | Point at an actual directory. |
WORK_FOLDER_NOT_READABLE | The path exists but the worker process can’t access it. | chmod / chown the directory so the worker user can read+execute it. |
AGENT_TYPE_UNSUPPORTED | agent.type is not in claude_code / codex. | Use a supported value. |
PERMISSIONS_UNSUPPORTED | agent.permissions is not in autonomous / approval. | Use a supported value (default is approval). |
AGENT_EXECUTABLE_NOT_FOUND | Agent CLI (claude / codex) can’t be resolved on PATH. | Install the agent; or override agents.<type>.executable in config.yaml. |
AGENT_CRASHED | Agent process exited unexpectedly (exit code in error.message). A failed resume on restart also surfaces here. | Inspect thread.log; reset the thread to pending to retry. |
THREAD_POST_FAILED | Persistent 4xx posting items to the thread. | Check session access; inspect thread.log for the rejected payload. |
THREAD_ITEM_TOO_LARGE | An item exceeds the cap even after truncation (very rare). | Inspect thread.log; reduce tool output size; reset the 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).
| Code | Cause |
|---|
API_TRANSIENT_ERROR | 5xx response from the BlobHub API. |
API_RATE_LIMITED | 429 response. |
API_NETWORK_ERROR | Connection failed or timed out. |
API_COMMAND_FAILED | Generic 4xx-not-otherwise-classified. |
Limitations
These trade-offs are specific to session_agent_harness. For generic worker limitations (no hot config
reload, single-process / single-machine), see Reference.
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:
delete_session_object(session_id, alias="worker")
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.
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:
workspace:
work_folder: /absolute/path/to/repo
agent:
type: claude_code
instance:
state: pending
There is no inline editor in blobhub-web for these fields in v1; a dedicated editor is a deferred fast-follow.
See also