Skip to content

<!-- CODE GENERATED BY cmd/harbor-gen-protocol-docs. DO NOT EDIT. -->

Protocol errors

The 18 canonical Harbor Protocol error codes, generated from the single-source registry (internal/protocol/errors). The HTTP column is read from the same code-to-status binding the wire transport serves — the two cannot drift.

Every error response body is the one Error envelope:

json
{ "code": "<stable code>", "message": "<human-readable, advisory>" }

Clients branch on code (stable across Runtime refactors — RFC §5.3), never on message.

CodeHTTPWhen it firesShould you retry?
agent_retired409An authorized agent-addressed operation selected a terminally retired agent configuration.No — choose a different agent; retirement is terminal.
agent_retirement_conflict409A retirement request used a stale active-content hash or a different operation id from the durable replay identity.Only by replaying agent_config.retire with the exact original operation id and expected content hash.
auth_rejected401A bearer token was present but failed verification: malformed, an algorithm outside the asymmetric allowlist, bad signature, expired / not-yet-valid, unknown kid, audience or issuer mismatch.Only after obtaining a fresh valid token.
identity_required401The request resolved no complete (tenant, user, session) identity scope — a missing bearer, a missing session (no X-Harbor-Session header and no default claim), or a body identity that contradicts the verified token. Identity is mandatory and fails closed.No — attach a token / session first (Auth & identity).
identity_scope_required403The request is authenticated and identified, but the requested cross-tenant fan-in (e.g. events.subscribe?admin=1) or admin verb needs a verified admin / console:fleet scope claim the token does not carry.No — re-authenticate with a scope-bearing token.
invalid_request400The request was structurally malformed: undecodable JSON, a wrong wire shape for the method, an out-of-range field.No — fix the request shape first.
not_found404The request's target does not exist in the caller's scope: a steering control for a run with no live inbox (never started or already terminal), an unknown task / flow / artifact id. Cross-tenant existence is never revealed — a foreign id is indistinguishable from a missing one.No — the target is gone or never existed for you.
payload_invalid422A control payload violated an RFC §6.3 bound (depth > 6, > 64 keys, > 50 list items, a string > 4096 chars, > 16 KiB total) or carried an unsupported leaf type.No — shrink / restructure the payload.
presign_unsupported501An artifacts.get_ref request reached an ArtifactStore driver without presigned-URL support (in-mem / fs / sqlite / postgres blob drivers). The resolver fails loud instead of silently streaming bytes.No — the configured driver cannot satisfy it; use a presign-capable store (S3 family) or download via the Console proxy.
request_too_large413An artifacts.put body exceeded the configured protocol.max_request_bytes bound. The upload is refused loudly, never truncated.No — shrink the payload or raise the operator-side bound.
revision_conflict409An agent_config.* write declared an expected_content_hash and the agent's active revision no longer carries it — another writer moved the base between the caller's read and its write — or the agent has no active revision at all. The request was well-formed and authorised; nothing was persisted (no revision, no active-pointer move, no agent.config.revised event). The refusal is exact across Runtime processes sharing a shipped StateStore: publication rechecks the active-pointer EventID through StateStore.SaveIf; the per-owner lock only reduces local contention. Omitting expected_content_hash keeps the unconditional last-writer-wins behaviour.Yes, after re-reading — call agent_config.get (or agent_config.user.get if the door you are retrying is a user.* twin; they are separate revision spines and a hash from the wrong one never matches) for the current revision_id and content_hash, re-apply your edit on top (agent_config.diff compares what you read against what it is now), and resubmit with the fresh hash.
runtime_error500An unclassified runtime-side failure — the catch-all. Also used on the SSE surface for subscriber-limit (429) and bus-closed (503) conditions.Yes, with backoff — the request shape is not the problem.
scope_mismatch403The caller's steering scope claim is below the control method's RFC §6.3 minimum, or a cross-tenant steering / mutation was attempted without admin.No — the operation needs a higher scope.
session_erased409A start named a session id that was permanently deleted by sessions.delete (right-to-erasure). The session is terminal and cannot be reopened — its data is gone. A closed-but-not-erased session, by contrast, reopens normally on start.No — the conversation was erased; start a new one with a fresh session id.
session_running409A sessions.delete erasure was refused because the target session has a RUNNING task, mirroring the GC never-reap-running invariant. No store is touched on refusal — a session with in-flight work is durable execution state, not a cache entry.Yes — re-issue after the session's task finishes (or cancel it first).
session_skill_cutover_pending409A session-personal skill mutation reached a tenant whose explicit durable cutover is still dual_read; Harbor refuses the mutation until the declared migration completes and a fresh verification pass authorizes state_only.Yes, after the operator completes the tenant's declared cutover; do not retry as an unconditional legacy write.
session_skill_read_unstable409All three bounded before/after lifecycle and session-erasure fence reads observed concurrent change. Harbor returned no partial session-skill view.Yes, after the concurrent lifecycle or erasure transition settles.
unknown_method404The method name is not in the canonical registry (methods.md).No — check the method name and this Runtime's advertised capabilities (runtime.info).

Apache-2.0 licensed — see LICENSE.