> ## 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>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.