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

# Errors, pagination and limits

> The error shape, what each status means and how to fix it, paging through lists, and rate limits.

## Error shape

Every `/v1` error has the same shape, with an HTTP status to match:

```json theme={null}
{
  "error": {
    "type": "conflict",
    "message": "A task is already running on this computer.",
    "active_task_id": "task_mW8mcFPUN7Of"
  }
}
```

`type` is stable and meant for your code. `message` is written for a person and may change; log it or show it, but don't match on it. Some errors add fields, such as `active_task_id` above, or `code` and `answered_by` when a question was already answered.

| Status | `type`             | Meaning                                                                      |
| ------ | ------------------ | ---------------------------------------------------------------------------- |
| 400    | `invalid_request`  | The request is malformed or a field is invalid.                              |
| 401    | `authentication`   | The API key is missing, wrong or revoked.                                    |
| 402    | `payment_required` | The org's plan doesn't allow this, for example its free minutes are used up. |
| 403    | `permission`       | Your role can't do this.                                                     |
| 404    | `not_found`        | No such agent, session, task or route in this org.                           |
| 409    | `conflict`         | The request conflicts with the current state.                                |
| 429    | `rate_limit`       | Too many requests, or too many tasks at once for the plan.                   |
| 500    | `server_error`     | Something went wrong on GuidingHand's side.                                  |

## Common causes and fixes

### 400 `invalid_request`

* **`"prompt" (string) is required`**: send a non-empty `prompt` when starting a task.
* **Metadata rejected**: `metadata` must be an object with up to 50 keys, keys up to 40 characters, and string values up to 500 characters. Numbers and nested objects aren't allowed; send `"42"`, not `42`.
* **Agent id rejected**: `agent_id` is 2 to 40 lowercase letters, numbers and dashes. `effort` is `low`, `medium`, `high` or `null`.
* **Webhook URL refused**: it must be `https` and resolve to a public address. See [Webhooks](/guides/webhooks#public-https-only).
* **`/respond` without an id**: send `question_id` with `answer`, or `approval_id` with `decision`.

### 401 `authentication`

Send `Authorization: Bearer gh_live_…` with an org API key from **Settings → API keys**. Check that the key wasn't revoked, and that you aren't sending a session token (`gh_sk_…`), which only works with the older [session-token API](/api-reference/introduction#the-older-session-token-api).

### 402 `payment_required`

Starting a task on the free plan after its free minutes are used, or in an org that has no free minutes. An owner can pick a plan in the console under **Settings → Plan and usage**. A running task on the free plan is also stopped when the minutes run out; its `stopped` event says so.

### 403 `permission`

The signed-in person's role is too low, for example a member deleting a session. API keys act with the admin role, so this comes up mainly with console sign-ins.

### 404 `not_found`

The id doesn't exist in the org the key belongs to. Session ids are codes like `K7QM-24XP`; task ids start with `task_`. A session whose code has expired returns `404` when you start a task on it: create a new session.

### 409 `conflict`

* **"No computer is connected to this session."** The session's `status` isn't `connected`. The customer may have quit the app, closed the laptop or lost the network. Wait for `session.connected`, or ask them to open GuidingHand.
* **"A task is already running on this computer."** One task runs at a time per computer. `error.active_task_id` is the running task: follow it to the end, or stop it with `POST /v1/tasks/{task_id}/stop`.
* **"An agent with that address already exists."** Pick another `agent_id`, or `PATCH` the existing agent.
* **"That was already answered by the person at the computer."** (`code: "already_answered"`, `answered_by: "customer"`) The customer answered the question, or decided the approval, on their screen before you did, and theirs is the one the task used. Nothing to fix: carry on following the task. With `answered_by: "operator"`, someone on your team (another process, or the console) answered first. See [questions the customer can answer](/concepts/agents#questions-the-customer-can-answer) and [approvals the customer can decide](/concepts/agents#approvals-the-customer-can-decide).
* **"That question/approval is not pending."** (`code: "not_pending"`) The id belongs to an earlier question, or the task has ended. Read the task again and respond to the current `pending`.

### 429 `rate_limit`

* Too many requests (see [rate limits](#rate-limits)). Wait and retry with backoff.
* Your plan's limit on tasks running at the same time across the org. Wait for a task to finish, or move to a plan with more.

### 500 `server_error`

Retry with backoff. If you start tasks with a `request_id`, retrying is safe: you get the same task rather than a second one.

## Tasks that fail

A `failed` task is not an HTTP error: the task ran and couldn't finish. `error` has the reason, for example:

* the computer disconnected in the middle of a step,
* macOS was dropping the app's clicks and typing (a permission problem the app reports and offers to fix with a restart),
* the task ran 150 steps without finishing,
* the server was restarted during the task. Start it again.

Retry by starting a new task with a new `request_id`. For recurring failures, the task's `replay_url` and `GET /v1/tasks/{task_id}?include=trace` show what happened; the trace is safe to share with GuidingHand support.

## Pagination

`GET /v1/sessions` and `GET /v1/tasks` return the newest first, one page at a time:

```json theme={null}
{
  "data": [ { "object": "task", "task_id": "task_mW8mcFPUN7Of", "...": "..." } ],
  "has_more": true,
  "next_cursor": "1790434012559"
}
```

* `limit` sets the page size, 1 to 100 (default 20).
* When `has_more` is `true`, pass `next_cursor` as `cursor` to get the next, older page. When it is `false`, `next_cursor` is `null`.
* Keep the same filters (`session_id`, `agent_id`, `status`) on every page.

<CodeGroup>
  ```javascript Node theme={null}
  let cursor = null;
  do {
    const qs = new URLSearchParams({ status: 'failed', limit: '100', ...(cursor ? { cursor } : {}) });
    const page = await gh('GET', `/v1/tasks?${qs}`);
    for (const t of page.data) console.log(t.task_id, t.error);
    cursor = page.has_more ? page.next_cursor : null;
  } while (cursor);
  ```

  ```python Python theme={null}
  cursor = None
  while True:
      params = {"status": "failed", "limit": 100}
      if cursor:
          params["cursor"] = cursor
      page = gh("GET", "/v1/tasks", params=params)
      for t in page["data"]:
          print(t["task_id"], t["error"])
      if not page["has_more"]:
          break
      cursor = page["next_cursor"]
  ```
</CodeGroup>

(`gh` is the helper from the [Quickstart](/quickstart).)

`GET /v1/agents` returns all agents in one page.

## Rate limits

| Limit                    | Applies to                                      |
| ------------------------ | ----------------------------------------------- |
| 30,000 requests per hour | All `/v1` requests from one IP address.         |
| 600 sessions per hour    | `POST /v1/sessions`, per org.                   |
| Your plan's concurrency  | Tasks running at the same time, across the org. |

Past a limit, requests return `429 rate_limit`. Long polls count as one request each, so following a task with `wait_ms=25000` uses about 144 requests an hour.
