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

# Handle errors

> Rejected requests versus failed runs in the Seltz Agent API

To handle everything that can go wrong with a run, branch on whether the
request itself was rejected or the run later failed, rather than assuming a
successful call means a usable answer.

Agent fails in two different places, and they need different handling: the
same split [Fetch](/fetch/guides/handle-errors) makes between a rejected
request and a failed page.

A **rejected request** raises immediately. Nothing runs, and nothing costs
anything: an empty [`query`](/api-reference/agent/start-run#body-query), an
unknown field, or a malformed
[`output_schema`](/api-reference/agent/start-run#body-output-schema-one-of-0)
wrapper all fail this way. A **failed run** does not raise:
[`create`](/api-reference/agent/start-run) and
[`get`](/api-reference/agent/get-run) return normally, and the run reaches a
terminal [`status`](/api-reference/agent/get-run#response-status):
`"failed"` with
[`stop_reason`](/api-reference/agent/get-run#response-stop-reason-one-of-1)
saying why.

<CodeGroup>
  ```python Python theme={null}
  from seltz import (
      AgentRunStatus,
      Seltz,
      SeltzAPIError,
      SeltzAuthenticationError,
      SeltzConnectionError,
      SeltzRateLimitError,
      SeltzTimeoutError,
  )

  client = Seltz()

  try:
      run = client.agent.create_and_wait(
          "What are the three most notable AI-company acquisitions announced in 2026?",
          timeout=120,
      )
  except SeltzAuthenticationError:
      print("Error: Invalid API key")
  except SeltzConnectionError:
      print("Error: Could not connect to Seltz API")
  except SeltzTimeoutError:
      print("Still running -- poll get() again later, or cancel() it")
  except SeltzRateLimitError:
      print("Error: Rate limit exceeded, try again later")
  except SeltzAPIError as e:
      print(f"Request rejected: {e.grpc_code} - {e.grpc_details}")
  else:
      if run.status == AgentRunStatus.AGENT_RUN_STATUS_COMPLETED:
          print(run.output.text)
      else:
          print("Run did not complete:", run.stop_reason)
  ```

  ```typescript TypeScript theme={null}
  import {
    AgentRunStatus,
    Seltz,
    SeltzAPIError,
    SeltzAuthenticationError,
    SeltzConnectionError,
    SeltzRateLimitError,
    SeltzTimeoutError,
  } from "seltz";

  const client = new Seltz();

  try {
    const run = await client.agent.createAndWait({
      query: "What are the three most notable AI-company acquisitions announced in 2026?",
      timeoutMs: 120000,
    });

    if (run.status === AgentRunStatus.COMPLETED) {
      console.log(run.output?.text);
    } else {
      console.log("Run did not complete:", run.stopReason);
    }
  } catch (error) {
    if (error instanceof SeltzAuthenticationError) {
      console.error("Error: Invalid API key");
    } else if (error instanceof SeltzConnectionError) {
      console.error("Error: Could not connect to Seltz API");
    } else if (error instanceof SeltzTimeoutError) {
      console.error("Still running -- call get() again later, or cancel() it");
    } else if (error instanceof SeltzRateLimitError) {
      console.error("Error: Rate limit exceeded, try again later");
    } else if (error instanceof SeltzAPIError) {
      console.error(`Request rejected: ${error.code} - ${error.message}`);
    } else {
      throw error;
    }
  }
  ```
</CodeGroup>

## Over REST

A rejected request is a non-200 with the same `{"error": {"code": ...,
"message": ...}}` envelope Search, Answer, Fetch, and Monitor use, so
shared error-handling code across endpoints works for Agent, too. Branch on
`code`; `message` is prose for logs and its wording can change.

```json theme={null}
{
  "error": {
    "code": "INVALID_REQUEST",
    "message": "`query` must be non-empty"
  }
}
```

The full status/code table is in [Reference](/agent/reference#errors).

## A failed run is not a rejection

Once the server accepts a run, its eventual failure arrives as data, not as
an error. Branch on `status`, then read `stop_reason` for why:

<CodeGroup>
  ```python Python theme={null}
  if run.status == AgentRunStatus.AGENT_RUN_STATUS_FAILED:
      print("Run failed:", run.stop_reason)
      # AgentRunStopReason.AGENT_RUN_STOP_REASON_INVALID_OUTPUT, _BUDGET_REACHED,
      # _TIMEOUT, or _INTERNAL_ERROR
  ```

  ```typescript TypeScript theme={null}
  if (run.status === AgentRunStatus.FAILED) {
    console.log("Run failed:", run.stopReason);
    // AgentRunStopReason.INVALID_OUTPUT, BUDGET_REACHED, TIMEOUT, or INTERNAL_ERROR
  }
  ```
</CodeGroup>

`internal_error` covers every fault on Seltz's side, including retrieval
outages. There is no finer split, so the only response is to retry the run. This requires a new `create`, since a failed run cannot resume.

A run that produces no JSON output at all fails
with `stop_reason: invalid_output` rather than returning something
unparsable.

## Next steps

* [Basic run](/agent/guides/basic-run): the full request/response cycle, including the cURL poll
* [Structured output](/agent/guides/structured-output): requesting a JSON result instead of Markdown
* [Reference](/agent/reference): endpoints, fields, limits and errors
* [Agent API Reference](/api-reference/agent/start-run): full REST request and response specification
