> ## Documentation Index
> Fetch the complete documentation index at: https://docs.doers.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> One envelope, stable codes, and what each one means.

Every REST error has the same shape. `code` is a stable machine name; `message` is English text for
a person and may change.

```json theme={null}
{
  "error": {
    "code": "invalid_input",
    "message": "The request did not match the expected shape.",
    "details": [
      { "field": "when", "message": "Invalid ISO date" },
      { "field": "priority", "message": "Too big: expected number to be <=4" }
    ]
  }
}
```

`details` is present on validation errors only. It names **every** offending field, not only the
first, with a dotted path for nested fields (`repeat.every`).

## Codes every route can return

| HTTP  | `code`               | When                                                                    |
| ----- | -------------------- | ----------------------------------------------------------------------- |
| `400` | `invalid_input`      | a field is missing, of the wrong type or out of range, or unknown       |
| `401` | `unauthorized`       | no credential, or one that is unknown, expired or revoked               |
| `404` | `not_found`          | no route at this path                                                   |
| `405` | `method_not_allowed` | the path exists under another method; the `Allow` header lists which    |
| `429` | `rate_limited`       | too many requests in the current window; wait for `Retry-After` seconds |
| `429` | `model_rate_limited` | too many model turns (coach operations) in the current window           |
| `500` | `internal_error`     | something failed on our side; the message is always generic             |
| `501` | `not_implemented`    | the operation is declared but has no implementation on this deployment  |

## Business codes

An operation that refuses for a reason of its own says which, with a specific code:
`task_not_found`, `insufficient_role`, `not_archived`, `seat_limit`. They use four statuses:

| HTTP  | Means                                                                                         |
| ----- | --------------------------------------------------------------------------------------------- |
| `403` | you can see the resource, but your role does not allow this action                            |
| `404` | it does not exist, **or** it belongs to someone who has not shared it with you                |
| `409` | it conflicts with the current state (already a member, slug taken, a request replayed)        |
| `422` | well formed but not usable (a title that is empty once parsed, deleting what is not archived) |

The API never confirms that another account's resource exists: that is a `404`, not a `403`.

<Accordion title="Every business code">
  94 codes, read from the API's handlers. A `${…}` in a message is shown as `…`.

  | Status | Code                             | Example message                                                                  |
  | ------ | -------------------------------- | -------------------------------------------------------------------------------- |
  | 400    | `bundle_too_large`               | Package too large: … KB for                                                      |
  | 400    | `duplicate_path`                 | Duplicate file in the package: “…”.                                              |
  | 400    | `grant_requires_handle`          | An explicit grant names one exact handle.                                        |
  | 400    | `invalid_completed_since`        | completedSince is only valid with status=done.                                   |
  | 400    | `invalid_cursor`                 | The task cursor is invalid.                                                      |
  | 400    | `unknown_permission`             | No such operation: “…”.                                                          |
  | 400    | `unsafe_path`                    | Refused file path “…”: ….                                                        |
  | 400    | `upload_token_failed`            |                                                                                  |
  | 401    | `unauthorized`                   | Missing bearer token.                                                            |
  | 403    | `calendar_event_read_only`       | Google says this event is read-only.                                             |
  | 403    | `calendar_not_connected`         | Google Calendar is not connected.                                                |
  | 403    | `device_revoked`                 | This device was revoked.                                                         |
  | 403    | `google_refused`                 | Google returned no access token. Reconnect Google Calendar.                      |
  | 403    | `guest_role_exceeded`            | A guest may view or comment, but may not edit.                                   |
  | 403    | `insufficient_role`              | Only the owner, or the workspace's owner or admins, change who reaches this.     |
  | 403    | `not_author`                     | Only the author may edit.                                                        |
  | 403    | `referral_not_eligible`          | Invites open after a free trial.                                                 |
  | 403    | `referral_own_code`              | An invite is for someone else.                                                   |
  | 403    | `wrong_account`                  | Sign in as … to accept this invitation.                                          |
  | 404    | `access_not_found`               | Nobody by that grant here.                                                       |
  | 404    | `agent_unavailable`              | This agent is not available.                                                     |
  | 404    | `area_not_found`                 | No such area.                                                                    |
  | 404    | `billing_customer_missing`       | This account has no billing customer to manage.                                  |
  | 404    | `billing_subscription_missing`   | This account has no current recurring subscription.                              |
  | 404    | `comment_not_found`              | No such comment.                                                                 |
  | 404    | `connection_not_found`           | No such connection.                                                              |
  | 404    | `file_not_found`                 | No such attachment.                                                              |
  | 404    | `follow_not_found`               | You did not follow them.                                                         |
  | 404    | `grant_not_found`                | No such send.                                                                    |
  | 404    | `habit_completion_not_found`     | Nothing was recorded for that day.                                               |
  | 404    | `habit_not_found`                | No such habit.                                                                   |
  | 404    | `heading_not_found`              | No such heading.                                                                 |
  | 404    | `install_not_found`              | No such install.                                                                 |
  | 404    | `invite_not_found`               | No such invitation.                                                              |
  | 404    | `join_link_not_found`            | This link is not open.                                                           |
  | 404    | `link_not_found`                 | No such link.                                                                    |
  | 404    | `listing_not_found`              | This Agent is not available.                                                     |
  | 404    | `member_not_found`               | No such collaborator.                                                            |
  | 404    | `not_found`                      | Not found.                                                                       |
  | 404    | `package_not_found`              | This share is no longer available.                                               |
  | 404    | `profile_not_found`              | No such profile.                                                                 |
  | 404    | `project_not_found`              | No such project.                                                                 |
  | 404    | `referral_not_found`             | No such invite code.                                                             |
  | 404    | `request_not_found`              | No such consent request.                                                         |
  | 404    | `round_not_found`                | Nothing has happened in that round yet.                                          |
  | 404    | `seat_not_found`                 | They had no seat of yours.                                                       |
  | 404    | `session_not_found`              | No such session.                                                                 |
  | 404    | `task_not_found`                 | No such task.                                                                    |
  | 404    | `token_not_found`                | No such token.                                                                   |
  | 404    | `user_not_found`                 | Nobody by that name. Use their email address to invite them.                     |
  | 404    | `version_not_found`              | That version is not published.                                                   |
  | 404    | `workspace_not_found`            | No such workspace.                                                               |
  | 409    | `already_author`                 | You are the author of this package.                                              |
  | 409    | `already_owner`                  | The owner already has every access.                                              |
  | 409    | `already_yours`                  | This is yours already: it is in your vault.                                      |
  | 409    | `billing_not_configured`         | This server has no complete Creem billing catalogue configured.                  |
  | 409    | `billing_product_unchanged`      | The subscription already uses this product.                                      |
  | 409    | `billing_purchase_exists`        | This account already owns this lifetime product.                                 |
  | 409    | `billing_subscription_ambiguous` | This account does not have one unambiguous recurring subscription to manage.     |
  | 409    | `billing_subscription_exists`    | This account already has a recurring subscription or checkout to manage.         |
  | 409    | `billing_units_not_supported`    | This billing product does not support a seat-count update.                       |
  | 409    | `coach_key_refused`              |                                                                                  |
  | 409    | `coach_refused`                  |                                                                                  |
  | 409    | `consent_required`               | This version also asks to …. Review it before updating.                          |
  | 409    | `digest_mismatch`                | This version changed while you were reading it. Read it again before updating.   |
  | 409    | `flame_own_session`              | A flame is for someone else's effort.                                            |
  | 409    | `google_client_missing`          | This server has no Google Calendar client configured.                            |
  | 409    | `habit_archived`                 | This habit is archived.                                                          |
  | 409    | `habit_not_due`                  | This habit is not due on that day.                                               |
  | 409    | `idempotency_mismatch`           | That key has already been used for a different command.                          |
  | 409    | `no_coach_key`                   | Add your Anthropic key in Settings › AI Coach.                                   |
  | 409    | `no_team_plan`                   | You hold no team plan.                                                           |
  | 409    | `owner_cannot_leave`             | Hand the workspace over first.                                                   |
  | 409    | `package_suspended`              | This agent has been suspended. What is on your disk stays; it cannot run here.   |
  | 409    | `package_unpublished`            | This package is no longer published.                                             |
  | 409    | `personal_workspace`             | A personal workspace keeps its owner's handle and its owner.                     |
  | 409    | `profile_self_follow`            | You cannot follow yourself.                                                      |
  | 409    | `referral_allowance_spent`       | You have made your … invites.                                                    |
  | 409    | `referral_expired`               | This invite has expired.                                                         |
  | 409    | `referral_redeemed`              | This invite has already been used.                                               |
  | 409    | `seat_limit`                     | Every one of the … seats is taken.                                               |
  | 409    | `seat_self`                      | Your own seat is the plan.                                                       |
  | 409    | `secrets_key_missing`            | This server has no SECRETS\_KEY configured, so it cannot store a key safely.     |
  | 409    | `session_running`                | A session is shared once it is over.                                             |
  | 409    | `slug_taken`                     | That address is already someone's.                                               |
  | 409    | `stale_preview`                  | This package changed since you read it. Open it again before installing.         |
  | 409    | `task_creation_request_consumed` | This creation request no longer refers to a task.                                |
  | 422    | `digest_required`                | Republish this version: it was frozen before content digests existed.            |
  | 422    | `empty_title`                    | The title is empty once the sentence is parsed.                                  |
  | 422    | `heading_parent_mismatch`        | That heading belongs to a different project or area than the task: move the task |
  | 422    | `licence_required`               | Choose a licence for this version before submitting it.                          |
  | 422    | `not_archived`                   | Archive the … before deleting it for good.                                       |
  | 422    | `one_parent_required`            | Provide exactly one of projectId or areaId.                                      |
  | 422    | `workspace_mismatch`             | A hierarchy move cannot cross workspace boundaries.                              |
</Accordion>

## Behaviours worth knowing

<Note>
  **Nothing is corrected silently.** A priority of `9` is a `400`, not a `4`. An unknown top-level
  field in a JSON body is a `400`: the input object is strict.
</Note>

<Note>
  **Unknown query parameters are ignored**, so tracking parameters do not break a request. A
  misspelled one is therefore dropped rather than reported: check your spelling against the
  reference.
</Note>

<Note>
  **A body that is not valid JSON is read as no body at all.** It fails with `invalid_input` when
  the operation requires a field, and is otherwise an empty input: a broken `PATCH` body is an empty
  patch.
</Note>

<Note>
  **The path wins.** If a JSON body repeats a path parameter (`PATCH /v2/tasks/{taskId}` with a
  `taskId` in the body), the value from the path is used.
</Note>

<Note>
  **A `500` never leaks anything.** Its message is always generic; the detail goes to the server's
  logs. If you can reproduce one, it is a bug.
</Note>

## Batches are all or nothing

`PATCH /v2/tasks`, `POST /v2/tasks/batch`, `POST /v2/tasks/complete`, `POST /v2/tasks/archive`,
`PATCH /v2/tasks/schedule` and `POST /v2/hierarchy/reorder` check every item before changing any.
If one task is out of reach, the whole request fails and nothing changes.

## Errors over MCP

The MCP server turns the same failures into tool results with `isError: true` and the message as
text, so the model can read it and correct itself. See [Protocol](/mcp/protocol).
