Unified Harness Protocol

Errors

Unified Harness Protocol, version 2026-10-19

A client's error handling is only as good as the server's error reporting. This chapter defines one envelope, a closed set of codes, and which failures are worth retrying.

1. The error envelope

Every non-2xx response MUST have this body:

{
  "error": {
    "type": "invalid_request_error",
    "code": "harness_not_found",
    "message": "No harness with id 'chrn_deadbeef'.",
    "param": "metadata.harness_id",
    "detail": null
  }
}
Field Type Required Meaning
type string yes Broad class; see §2
code string yes Specific, machine-readable reason; see §3
message string yes Human-readable, one sentence, no stack trace
param string | null yes Dotted path to the offending field, when there is one
detail object | null yes Structured extra context, or null

Rules:

  • message MUST be safe to show a user. It MUST NOT contain credentials, internal hostnames, file paths, or stack traces.
  • code MUST come from §3 where one applies. A server MAY define additional codes for conditions this specification does not cover, and MUST namespace them with a vendor prefix (acme_…) so a future version of this specification cannot collide with them.
  • A server MUST NOT return 200 with an error inside. The failure of a task is reported as a response with status: "failed" and HTTP 200, because the request succeeded; the failure of a request is a non-2xx with this envelope. Those are different events and MUST NOT be conflated.

Compatibility note. A server MAY additionally include a top-level detail string alongside error, for clients written against implementations that predate this envelope. That field is deprecated, carries no information not in error.message, and will be removed in a future version.

2. Error types

type HTTP Meaning
invalid_request_error 400, 404, 409, 413, 422 The request was wrong. Retrying it unchanged will fail again.
authentication_error 401 The credential is missing, malformed or unknown.
permission_error 403 Authenticated, but not allowed to do this.
rate_limit_error 429 Too many requests, or a quota is exhausted.
harness_error 200 (in a failed response) The harness ran and failed.
server_error 500, 502, 503, 504 The server failed. Retrying MAY succeed.

3. Error codes

3.1 Request and routing

Code HTTP Meaning
unsupported_protocol_version 400 UHP-Version names a version this server cannot serve. detail.supported lists what it can.
invalid_input 400 The body could not be parsed, or a field has the wrong type.
harness_not_found 404 No such harness in the caller's scope.
response_not_found 404 No such response in the caller's scope.
session_not_found 404 No such session in the caller's scope.
plugin_not_found 404 The harness has no plugin of that name.
file_not_found 404 No such file in the caller's scope.
session_expired 404 The session existed but is past its retention.
harness_mismatch 409 The task named a different harness than the session it continues.
session_busy 409 A task is already running in this session.
harness_busy 409 A harness that retires its sessions with it has a task in progress; it is not retired mid-task (Harnesses §5.3).
plugin_conflict 409 Two plugins share a name, or a component name collides across the harness and its plugins. detail.component, detail.name and detail.between say which.
file_too_large 413 The upload exceeds the server's limit. detail.max_bytes states it.
model_unavailable 422 The requested model cannot be served for this harness.
model_not_available 422 A harness create or update named a default_model its base does not run (Harnesses §5.1).
unsupported_base 422 The requested harness base is not supported by this server.
environment_not_found 404 No such environment in the caller's scope, on a read, a harness write or a task (Environments §8).
environment_not_ready 409 A task on an environment with no active version, or activating an unfinished build.
environment_busy 409 A build is running: a second build or a delete must wait.
environment_exists 409 The name's slug is already another environment's mount path.
environment_invalid 422 A name that is no path segment, a file path that escapes the root, or an import that is neither archive nor repository.
environment_unavailable 502 The server's build or storage backend did not answer.
memory_not_found 404 No such memory, or one the caller holds no privilege on: the two are one answer (Memories §12).
memory_forbidden 403 The caller sees the memory and lacks the privilege the operation needs.
memory_record_not_found 404 No such record in this memory.
memories_not_attached 404 The agent holds no memory: nothing was granted to it.
memory_invalid 422 A parent that would make a cycle, two default entries, a record with no content, a query whose parameters do not match.
memory_unsupported 422 An operation the memory's provider does not implement at all. A partial answer is reported in degraded, not as this.
memory_busy 409 A move or a delete while a consolidation runs.
memory_unavailable 502 or 503 The memory's provider did not answer, or is not connected.
plugin_invalid 422 The package is not a valid Agent Plugins package: no manifest, a fatal manifest error, a path that escapes the root, or a name that contradicts the manifest. detail.path and detail.reason say where.
unsupported_plugin_schema 422 The manifest's $schema names an Agent Plugins version this server does not install. detail.supported lists what it does.
unsupported_transport 422 A plugin declares an MCP server whose transport cannot be run for this harness base. detail.transport and detail.base say which.

model_not_available and model_unavailable are refused at different moments and are not interchangeable. model_not_available refuses a request that writes a harness (a create or update whose default_model its base does not run), before any task exists; model_unavailable refuses a task whose model cannot be served for its harness, when the task is sent. The first tells the client to fix the harness, the second to send the task with another model.

3.2 Authentication and limits

Code HTTP Meaning
missing_credential 401 No bearer token.
invalid_credential 401 The token is malformed, unknown, or revoked.
insufficient_scope 403 The credential cannot perform this operation.
rate_limited 429 Slow down. Retry-After SHOULD be set.
quota_exhausted 429 A budget is spent; retrying now will not help.

3.3 Execution

Code Where Meaning
harness_error failed response The harness ran and could not complete the work.
harness_unavailable 503 No capacity to run this harness right now.
provider_error failed response The upstream model provider refused or failed.
timeout failed response The task exceeded its wall-clock budget.
cancelled cancelled response The client cancelled it.
preview_unavailable 501 This server does not render previews.
preview_failed 502 Conversion of this file failed.

4. Retrying

Situation Retry?
server_error (500, 502, 503, 504) Yes, with exponential backoff and jitter
rate_limited Yes, after Retry-After
quota_exhausted No — nothing will change until the quota does
session_busy Yes, once the in-flight task is terminal
Any invalid_request_error No — fix the request
authentication_error No — fix the credential
A task that reached failed Only with the client's own judgment; it consumed real resources

Retries of POST /v1/responses MUST carry an Idempotency-Key. Without one, a retry after a timeout runs the task a second time — and the first may still be running, editing the same files. This is the single most damaging mistake a UHP client can make; see Tasks §6.

5. Timeouts

A client SHOULD set generous timeouts. Agent tasks routinely run for minutes; a 30-second HTTP timeout will cancel healthy work.

  • Streaming: no total timeout. Use an inactivity timeout instead — a server SHOULD emit a comment line (: keep-alive) at least every 30 seconds so the client can distinguish a working agent from a dead connection.
  • Non-streaming: at least the task's timeout_seconds, plus margin.
  • A client that gives up MUST NOT assume the task stopped. It has not. Cancel it explicitly.
Unified Harness Protocol 2026-10-19 · Apache-2.0 · defined and maintained in the HarnessRouter open-source repository. The standard can be implemented independently of any hosted service.