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

# Errors

> Full reference for Semji API error codes, HTTP status codes, error response format, and handling strategies including retries and validation errors.

When a request cannot be completed, the Semji API returns a JSON error object alongside an appropriate HTTP status code. All errors follow the same structure, making them straightforward to handle in code. This page documents every error code the API can return, explains what triggers each one, and provides guidance on how to respond.

## Error response format

Every error response — regardless of cause or status code — uses the following shape:

```json theme={null}
{
  "error": {
    "code": "string",
    "message": "string"
  }
}
```

<ResponseField name="error.code" type="string">
  A stable, machine-readable string identifying the error type. Use this field in your error-handling logic — it will not change between API versions.
</ResponseField>

<ResponseField name="error.message" type="string">
  A human-readable explanation of the error. For `validation_error` responses, this describes the specific field that failed validation. Useful for debugging but not guaranteed to be stable across releases.
</ResponseField>

## Error codes reference

| HTTP status | `error.code`          | Meaning                                                     |
| ----------- | --------------------- | ----------------------------------------------------------- |
| 400         | `bad_request`         | The request was malformed or a required field is missing    |
| 401         | `unauthorized`        | API key is missing, invalid, or revoked                     |
| 403         | `forbidden`           | Authenticated but not permitted to access this resource     |
| 404         | `not_found`           | The resource does not exist                                 |
| 409         | `conflict`            | Request conflicts with current state, e.g. version mismatch |
| 422         | `validation_error`    | Request body failed schema validation                       |
| 429         | `rate_limited`        | Too many requests — hourly or burst limit exceeded          |
| 500         | `internal_error`      | Unexpected server error                                     |
| 502         | `bad_gateway`         | The upstream service returned an error                      |
| 503         | `service_unavailable` | Service temporarily unavailable                             |

***

## Detailed error reference

<AccordionGroup>
  <Accordion title="400 Bad Request — bad_request">
    Returned when the request is structurally malformed — for example, a required query parameter is missing, a path parameter cannot be parsed, or the request body is not valid JSON.

    ```json title="Example response" theme={null}
    {
      "error": {
        "code": "bad_request",
        "message": "The request was invalid."
      }
    }
    ```

    **How to handle:** Inspect the `message` field for details on what is wrong. Check that all required parameters are present and that your request body is valid JSON with the correct `Content-Type: application/json` header.
  </Accordion>

  <Accordion title="401 Unauthorized — unauthorized">
    Returned when the `Authorization` header is absent, does not begin with `Bearer sk_`, or contains a key that is invalid or has been revoked.

    ```json title="Example response" theme={null}
    {
      "error": {
        "code": "unauthorized",
        "message": "A valid API key is required. Use Authorization: Bearer sk_xxx."
      }
    }
    ```

    **How to handle:** Verify that your `Authorization` header is formatted correctly (`Bearer sk_...`), that the key has not been revoked in **Settings > Organization > API Keys**, and that you are not accidentally including extra whitespace or newline characters in the header value. See [Authentication](/api-reference/authentication) for details.
  </Accordion>

  <Accordion title="403 Forbidden — forbidden">
    Returned when the API key is valid but the authenticated user does not have permission to access the requested resource. Common causes: accessing a workspace the user is not a member of, or attempting an admin-only action with a Member-role key.

    ```json title="Example response" theme={null}
    {
      "error": {
        "code": "forbidden",
        "message": "You do not have access to this resource."
      }
    }
    ```

    **How to handle:** Confirm that the user associated with your API key has the required role in the workspace or organization. To check your current user and role, call `GET /v1/me` and `GET /v1/users`.
  </Accordion>

  <Accordion title="404 Not Found — not_found">
    Returned when the resource identified by the URL or a path parameter does not exist, or the authenticated user cannot see it.

    ```json title="Example response" theme={null}
    {
      "error": {
        "code": "not_found",
        "message": "The requested resource was not found."
      }
    }
    ```

    **How to handle:** Double-check the ID in your request path. Note that resources in workspaces you don't have access to also return `404` rather than `403`, to avoid leaking information about what exists.
  </Accordion>

  <Accordion title="409 Conflict — conflict">
    Returned when the request conflicts with the current state of the resource. The most common cause is an **optimistic locking conflict** on content updates: the `version` field you submitted does not match the current version on the server, meaning another client updated the resource since you last fetched it.

    ```json title="Example response" theme={null}
    {
      "error": {
        "code": "conflict",
        "message": "The request conflicts with the current state."
      }
    }
    ```

    **How to handle:** Re-fetch the resource to get the latest `version` value, apply your changes to the fresh copy, and retry the update with the new `version`.
  </Accordion>

  <Accordion title="422 Unprocessable Entity — validation_error">
    Returned when the request body is structurally valid JSON but fails schema validation — for example, an enum field contains an unrecognized value, a required field is `null`, or a string exceeds its maximum length. The `message` field names the specific field that failed.

    ```json title="Example response" theme={null}
    {
      "error": {
        "code": "validation_error",
        "message": "title: String must contain at least 1 character(s)"
      }
    }
    ```

    **How to handle:** Read the `message` to identify the offending field and correct the value in your request. Do not retry without fixing the input — the same request will fail again.
  </Accordion>

  <Accordion title="429 Too Many Requests — rate_limited">
    Returned when you exceed the rate limit for your API key — either 1,000 requests per rolling hour or 20 requests per second. The response includes a `Retry-After` header indicating how many seconds to wait.

    ```json title="Example response" theme={null}
    {
      "error": {
        "code": "rate_limited",
        "message": "Too many requests. Please try again later."
      }
    }
    ```

    **How to handle:** Wait at least the number of seconds in the `Retry-After` header before retrying. Use exponential backoff with jitter for repeated 429s. See [Rate Limits](/api-reference/rate-limits) for full retry guidance.
  </Accordion>

  <Accordion title="500 Internal Error — internal_error">
    Returned when an unexpected error occurs on the server. This is not caused by your request.

    ```json title="Example response" theme={null}
    {
      "error": {
        "code": "internal_error",
        "message": "An internal error occurred."
      }
    }
    ```

    **How to handle:** Retry the request using exponential backoff. If the error persists, contact Semji support.
  </Accordion>

  <Accordion title="502 Bad Gateway — bad_gateway">
    Returned when the Semji gateway successfully received your request but the upstream service returned an error. This is typically a transient infrastructure issue.

    ```json title="Example response" theme={null}
    {
      "error": {
        "code": "bad_gateway",
        "message": "The upstream service returned an error."
      }
    }
    ```

    **How to handle:** Retry with exponential backoff. A `502` is not caused by your request and will usually resolve within seconds.
  </Accordion>

  <Accordion title="503 Service Unavailable — service_unavailable">
    Returned when the API is temporarily unavailable, typically due to planned maintenance or an unexpected outage.

    ```json title="Example response" theme={null}
    {
      "error": {
        "code": "service_unavailable",
        "message": "The service is temporarily unavailable."
      }
    }
    ```

    **How to handle:** Retry after a short delay. Check the Semji status page for any active incidents.
  </Accordion>
</AccordionGroup>

## Handling errors in code

The following pattern covers the most important cases — retry on transient server errors, respect rate limit headers, and surface actionable messages for client errors:

<CodeGroup>
  ```typescript title="TypeScript" theme={null}
  const RETRYABLE_STATUSES = new Set([429, 500, 502, 503]);

  async function semjiRequest(method: string, path: string, body?: unknown) {
    const url = `https://api.semji.com/v1${path}`;

    for (let attempt = 0; attempt < 5; attempt++) {
      const response = await fetch(url, {
        method,
        headers: {
          Authorization: `Bearer ${process.env.SEMJI_API_KEY}`,
          "Content-Type": "application/json",
        },
        body: body ? JSON.stringify(body) : undefined,
      });

      if (RETRYABLE_STATUSES.has(response.status)) {
        const retryAfter = parseInt(response.headers.get("Retry-After") ?? "2", 10);
        const wait = (retryAfter + Math.random()) * 1000;
        await new Promise((r) => setTimeout(r, wait));
        continue;
      }

      if (!response.ok) {
        const { error } = await response.json();
        throw new Error(`[${error.code}] ${error.message}`);
      }

      return response.json();
    }

    throw new Error("Request failed after 5 retries");
  }
  ```

  ```python title="Python" theme={null}
  import time
  import random
  import requests

  RETRYABLE_STATUSES = {429, 500, 502, 503}

  def semji_request(method: str, path: str, **kwargs) -> dict:
      url = f"https://api.semji.com/v1{path}"
      headers = {"Authorization": f"Bearer {API_KEY}"}

      for attempt in range(5):
          response = requests.request(method, url, headers=headers, **kwargs)

          if response.status_code in RETRYABLE_STATUSES:
              retry_after = int(response.headers.get("Retry-After", 2 ** attempt))
              time.sleep(retry_after + random.uniform(0, 1))
              continue

          if not response.ok:
              error = response.json()["error"]
              raise ValueError(f"[{error['code']}] {error['message']}")

          return response.json()

      raise RuntimeError("Request failed after 5 retries")
  ```
</CodeGroup>

**Key rules:**

* **Retry** on `429`, `500`, `502`, and `503` using exponential backoff
* **Do not retry** on `400`, `401`, `403`, `404`, `409`, or `422` — these indicate a problem with the request that must be fixed first
* **Re-fetch then retry** on `409` — get the latest resource version before submitting your update again
* **Check `error.message`** on `422` to identify which field failed validation
