> ## 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.

# Reference

> session_agent_harness error codes and limitations

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](/worker/reference) 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](/worker/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.

### 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:

```yaml theme={null}
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

* [Reference](/worker/reference)
* [Recovery](/worker/session-agent-harness/recovery)
* [Handoff](/worker/session-agent-harness/thread-handoff)
