Pre-release. v0.1 is not out yet, so there is nothing to install and no public source to clone — the quickstart builds from a checkout.
Errors
The control plane answers errors in two shapes, depending on the endpoint.
OAuth errors
Section titled “OAuth errors”The token, introspection and revocation endpoints answer with RFC 6749 §5.2 error bodies: a code and a description for developers. The description never contains a token, a key or a stack trace.
{ "error": "invalid_scope", "error_description": "No requested scope is both held by the user and allowed for the agent."}| Error | Status | Meaning |
|---|---|---|
invalid_request |
400 |
A parameter is missing, repeated, or outside its limits |
invalid_client |
401 |
Client authentication failed |
invalid_grant |
400 |
The subject token is expired, invalid, from an untrusted issuer, or has no value for the sponsor key claim; or the task grant is unknown, revoked, or belongs to another agent |
unsupported_grant_type |
400 |
The grant_type is not token exchange or refresh_token |
invalid_scope |
400 |
The scope intersection is empty, or a refresh asked for a scope the task does not hold |
invalid_target |
400 |
The audience is not in the agent’s allowed_audiences, or a refresh names a different audience than the task’s |
access_denied |
400 |
The agent is disabled, the task is revoked or expired, the human is one Subact ID will not act for, or the task has less than five seconds left (task_ending: stop, do not retry). On an exchange, no task was created |
unsupported_token_type |
400 |
On revocation, a token_type_hint other than access_token or refresh_token |
temporarily_unavailable |
503 |
The decision cannot be made now: the identity provider’s keys, the agent’s keys or (in poll mode) the identity provider’s answer about the human could not be fetched, the database did not answer, or the instance is at capacity. Retry |
slow_down |
429 |
Too many requests from this source. Retry-After says when to retry |
temporarily_unavailable is never treated as a yes. Retry the same request; do not get a new
credential. When the database or capacity is the cause, the response carries Retry-After.
invalid_client
Section titled “invalid_client”invalid_client covers several causes on purpose: an unknown agent, a bad signature, a replayed
assertion jti, an assertion that claims to live longer than five minutes, and an instance
claim longer than 128 characters. Telling them apart would reveal which agent ids exist. The
audit ledger records the exact reason.
A disabled agent gets access_denied, not invalid_client, and only when its assertion
verified. Only the agent itself can learn that it is disabled.
Validation errors
Section titled “Validation errors”The admin API answers 400 with an RFC 9110 problem document that lists every invalid field:
{ "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1", "title": "The request is invalid.", "status": 400, "errors": { "display_name": ["is required."], "max_delegation_depth": ["is required."] }}Field names are the JSON names you sent. Validation runs in two passes: first the request’s shape, then the registration as a whole. Errors from the second pass appear only once the first pass succeeds:
{ "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1", "title": "The request is invalid.", "status": 400, "errors": { "sponsor_required": [ "must be true: issuing without a human subject token is not supported yet." ] }}Registration rules:
| Field | Rejected when |
|---|---|
agent_id |
Not 1 to 128 lowercase letters, digits, -, _ or ., starting with a letter or digit |
sponsor_required |
false. v0.1 issues nothing without a human subject token |
allowed_scopes |
Empty, containing duplicates, or containing a scope with spaces, quotes, backslashes or non-printable characters |
allowed_audiences |
Empty, containing duplicates, or containing anything but an absolute http or https URL |
max_task_ttl |
Outside SubactId:Agents:MinTaskTtl…MaxTaskTtl (one minute to one day by default) |
max_token_ttl |
Outside SubactId:Agents:MinTokenTtl…MaxTokenTtl (30 seconds to one hour by default), or longer than max_task_ttl |
max_delegation_depth |
Missing, or outside 1 to 5 |
jwks_uri |
Not an absolute HTTPS URL |
jwks |
Empty, or any key carrying a private member (d, p, q, dp, dq, qi, k, oth) |
jwks and jwks_uri |
Both set, or neither |
Finding the reason
Section titled “Finding the reason”Every authorization decision is an audit record, and every denial carries a machine-readable
reason. To see why a request was refused, query the ledger:
curl -s "https://subactid.internal.example.com/audit?decision=deny&limit=5" \ -H "Authorization: Bearer $SUBACTID_ADMIN_KEY"See the audit ledger and section 8 of the spec.
Tool server errors
Section titled “Tool server errors”A tool server’s refusals are its own. @subactid/server and @subactid/mcp answer 401, 403 or 503
(when keys or introspection cannot be reached) with a WWW-Authenticate header and a stable
reason. See the @subactid/server reference.
© 2026 Nikola Živković PR Agencija za programerske usluge Novi Sad. Subact ID is its product.