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

# Error Codes

> Error response format, error codes, and troubleshooting guide for CRAFT APIs.

# Error Codes

All CRAFT APIs return consistent error responses. This page documents the error format, all error codes, and troubleshooting guidance for each.

## Error Response Format

Error responses follow a standard JSON structure:

```json theme={null}
{
  "error": {
    "code": "RESOURCE_NOT_FOUND",
    "message": "Agent with ID '550e8400-e29b-41d4-a716-446655440000' not found.",
    "status": 404,
    "details": {
      "resource_type": "artifact",
      "resource_id": "550e8400-e29b-41d4-a716-446655440000"
    },
    "request_id": "req-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736"
  }
}
```

| Field        | Type    | Description                                     |
| ------------ | ------- | ----------------------------------------------- |
| `code`       | string  | Machine-readable error code                     |
| `message`    | string  | Human-readable error description                |
| `status`     | integer | HTTP status code                                |
| `details`    | object  | Additional context (varies by error type)       |
| `request_id` | string  | Unique request identifier for support           |
| `trace_id`   | string  | OpenTelemetry trace ID for end-to-end debugging |

## HTTP Status Codes

| Status | Meaning               | When Returned                                                         |
| ------ | --------------------- | --------------------------------------------------------------------- |
| `400`  | Bad Request           | Invalid request body, missing required fields, malformed parameters   |
| `401`  | Unauthorized          | Missing, expired, or invalid JWT token                                |
| `403`  | Forbidden             | Valid token but insufficient permissions                              |
| `404`  | Not Found             | Resource does not exist or is not accessible                          |
| `409`  | Conflict              | Resource already exists or version conflict                           |
| `412`  | Precondition Failed   | `If-Match` provided but does not match current resource version (PUT) |
| `413`  | Payload Too Large     | Uploaded body exceeds the server's size limit (artifact upload)       |
| `422`  | Unprocessable Entity  | Request is well-formed but semantically invalid                       |
| `428`  | Precondition Required | PUT without `If-Match` header (concurrency control required)          |
| `429`  | Too Many Requests     | Rate limit exceeded                                                   |
| `500`  | Internal Server Error | Unexpected server error                                               |
| `502`  | Bad Gateway           | Upstream service (Keycloak, OpenFGA) unavailable                      |
| `503`  | Service Unavailable   | Service is starting up or shutting down                               |

## Error Codes Reference

<Note>
  The OpenAPI specifications define a base `ErrorCode` enum with 9 values: `INVALID_REQUEST`, `INVALID_TOKEN`, `INSUFFICIENT_PERMISSIONS`, `RESOURCE_NOT_FOUND`, `RESOURCE_ALREADY_EXISTS`, `VALIDATION_ERROR`, `RATE_LIMIT_EXCEEDED`, `SERVICE_UNAVAILABLE`, `INTERNAL_ERROR`. The implementation may return more specific codes within each category as documented below.
</Note>

### Authentication Errors (401)

| Code              | Message                             | Troubleshooting                                                                       |
| ----------------- | ----------------------------------- | ------------------------------------------------------------------------------------- |
| `UNAUTHENTICATED` | No authentication token provided    | Include `Authorization: Bearer <token>` header                                        |
| `TOKEN_EXPIRED`   | Access token has expired            | Refresh the token or obtain a new one                                                 |
| `TOKEN_INVALID`   | Token signature verification failed | Ensure the token was issued by the correct Keycloak realm                             |
| `TOKEN_MALFORMED` | Token format is invalid             | Check that the token is a valid JWT (three base64-encoded segments separated by dots) |

### Authorization Errors (403)

| Code                    | Message                                     | Troubleshooting                                                                                        |
| ----------------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `FORBIDDEN`             | Insufficient permissions for this operation | Check your role assignment on the target project; see [RBAC Configuration](/guides/rbac-configuration) |
| `PROJECT_ACCESS_DENIED` | No access to the specified project          | Verify the project ID in the request matches a project you have been granted access to                 |
| `ORG_ACCESS_DENIED`     | Token does not belong to this organization  | Authenticate against the correct Keycloak realm for the target organization                            |

### Resource Errors (404, 409)

| Code                      | Message                                       | Troubleshooting                                                       |
| ------------------------- | --------------------------------------------- | --------------------------------------------------------------------- |
| `RESOURCE_NOT_FOUND`      | Resource not found                            | Verify the resource ID and ensure you have `can_read` permission      |
| `RESOURCE_ALREADY_EXISTS` | Resource with this identifier already exists  | Use a unique name or identifier; use PUT to update existing resources |
| `VERSION_CONFLICT`        | Resource has been modified by another request | Re-fetch the resource and retry with the current version              |

### Validation Errors (400, 422)

| Code                     | Message                        | Troubleshooting                                                   |
| ------------------------ | ------------------------------ | ----------------------------------------------------------------- |
| `VALIDATION_ERROR`       | Request body validation failed | Check the `details` field for specific field-level errors         |
| `INVALID_PARAMETER`      | Invalid query parameter        | Verify parameter name, type, and allowed values                   |
| `MISSING_REQUIRED_FIELD` | Required field is missing      | Include all required fields in the request body                   |
| `INVALID_FORMAT`         | Field value format is invalid  | Check the expected format (UUID, URL, email, etc.)                |
| `SEARCH_QUERY_INVALID`   | Malformed search query         | Use plain text for search queries; special operators are reserved |

### Rate Limiting Errors (429)

| Code                  | Message               | Troubleshooting                                                             |
| --------------------- | --------------------- | --------------------------------------------------------------------------- |
| `RATE_LIMIT_EXCEEDED` | Rate limit exceeded   | Wait for the `Retry-After` header duration; implement exponential backoff   |
| `QUOTA_EXCEEDED`      | Tenant quota exceeded | Contact your administrator to increase the quota for the exceeded dimension |

### Server Errors (500, 502, 503)

| Code                   | Message                         | Troubleshooting                                                                                          |
| ---------------------- | ------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `INTERNAL_ERROR`       | An unexpected error occurred    | Retry the request; if persistent, report with `request_id` and `trace_id`                                |
| `UPSTREAM_UNAVAILABLE` | Upstream service is unavailable | Keycloak or OpenFGA may be down; check service health. Governance down causes Assets/Utils to return 403 |
| `SERVICE_STARTING`     | Service is initializing         | Wait for the service to complete startup; check readiness probe                                          |
| `DATABASE_ERROR`       | Database operation failed       | Retry the request; if persistent, check database connectivity                                            |

## Handling Errors in Code

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from em_runtime_assets_sdk import AuthenticatedClient
    from em_runtime_assets_sdk.api.artifacts import get_artifact
    from em_runtime_assets_sdk.errors import UnexpectedStatus

    client = AuthenticatedClient(
        base_url="https://<platform-host>/api/assets",
        token=token,
    )

    try:
        agent = await get_artifact.asyncio(client=client, artifact_uri=artifact_uri)
    except UnexpectedStatus as e:
        if e.status_code == 401:
            # Token expired -- refresh and rebuild the client (token is immutable).
            token = await refresh_token()
            client = AuthenticatedClient(
                base_url="https://<platform-host>/api/assets", token=token
            )
            agent = await get_artifact.asyncio(client=client, artifact_uri=artifact_uri)
        elif e.status_code == 404:
            print(f"Not found: {e.content!r}")
        elif e.status_code == 429:
            # Rate limited -- read Retry-After from response headers
            retry_after = int(e.response.headers.get("Retry-After", "1"))
            await asyncio.sleep(retry_after)
            agent = await get_artifact.asyncio(client=client, artifact_uri=artifact_uri)
        else:
            print(f"HTTP {e.status_code}: {e.content!r}")
            raise
    ```

    <Note>
      `UnexpectedStatus.content` is raw `bytes`; decode and `json.loads()` to read the
      envelope (`{"error": {"code", "message", "request_id"}}`).
    </Note>
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    import { Client, ApiError } from '@emergence-ai/em-runtime-assets-sdk';
    import { getArtifact } from '@emergence-ai/em-runtime-assets-sdk/api';

    const client = new Client({
      BASE: 'https://<platform-host>/api/assets',
      TOKEN: accessToken,
    });

    try {
      const agent = await getArtifact({ client, artifactUri });
    } catch (error) {
      if (error instanceof ApiError) {
        const body = error.body as
          | { error?: { code?: string; message?: string; request_id?: string } }
          | undefined;
        switch (error.status) {
          case 401:
            // Token expired -- refresh and rebuild the client
            break;
          case 404:
            console.log(`Not found: ${body?.error?.message}`);
            break;
          case 429: {
            const retryAfter = Number(error.headers?.['retry-after'] ?? '1');
            await sleep(retryAfter * 1000);
            break;
          }
          default:
            console.error(`HTTP ${error.status} (${body?.error?.code}): ${body?.error?.message}`);
            console.error(`Request ID: ${body?.error?.request_id}`);
        }
      }
    }
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    # Check the HTTP status code
    HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
      -H "Authorization: Bearer $TOKEN" \
      "https://<platform-host>/api/assets/artifacts/$ARTIFACT_URI")

    if [ "$HTTP_STATUS" -eq 401 ]; then
      echo "Token expired, refreshing..."
      TOKEN=$(refresh_token)
    elif [ "$HTTP_STATUS" -eq 404 ]; then
      echo "Agent not found"
    elif [ "$HTTP_STATUS" -eq 429 ]; then
      echo "Rate limited, waiting..."
      sleep 60
    fi
    ```
  </Tab>
</Tabs>

## Retry Strategy

For transient errors (429, 500, 502, 503), implement exponential backoff with jitter:

| Attempt     | Wait Time          | Max Wait   |
| ----------- | ------------------ | ---------- |
| 1st retry   | 1 second + jitter  | 2 seconds  |
| 2nd retry   | 2 seconds + jitter | 4 seconds  |
| 3rd retry   | 4 seconds + jitter | 8 seconds  |
| 4th retry   | 8 seconds + jitter | 16 seconds |
| Max retries | 5                  | 30 seconds |

<Tip>
  For 429 errors, always respect the `Retry-After` header rather than using exponential backoff. The header provides the exact wait time until the rate limit window resets.
</Tip>

## Getting Help

When reporting an issue, include:

1. The `request_id` from the error response
2. The `trace_id` for end-to-end correlation
3. The timestamp of the request
4. The full error response body

## Next Steps

<CardGroup cols={2}>
  <Card title="API Overview" icon="rocket" href="/api-reference/introduction">
    Review the full API structure and endpoints.
  </Card>

  <Card title="API Authentication" icon="lock" href="/api-reference/authentication">
    Learn how to obtain and manage JWT tokens.
  </Card>

  <Card title="Python SDK" icon="python" href="/sdks/python">
    Use the Python SDK with built-in error handling.
  </Card>

  <Card title="TypeScript SDK" icon="js" href="/sdks/typescript">
    Use the TypeScript SDK with built-in error handling.
  </Card>
</CardGroup>
