# Error Handling https://api-docs.lumar.io/docs/graphql/error-handling The Lumar GraphQL API follows the standard GraphQL error response conventions. Understanding how errors are structured will help you build resilient integrations. ## GraphQL error response structure When a query fails, the response includes an `errors` array alongside (or instead of) the `data` field. Each error object contains a `message` string and an optional `extensions` object with a machine-readable `code`. ```graphql query GetProject($id: ObjectID!) { getProject(id: $id) { id name } } ``` **Variables:** ```json { "id": "invalid-id" } ``` **Response:** ```json { "errors": [ { "message": "Project not found.", "extensions": { "code": "BAD_USER_INPUT" } } ], "data": { "getProject": null } } ``` Key points: - Partial data is possible -- some fields may resolve successfully while others produce errors. - The `extensions.code` field provides a stable identifier you can use for programmatic error handling. ## HTTP status codes | Status | Meaning | | ------- | ---------------------------------------------------------------------- | | **200** | Request completed. Check the `errors` array for partial failures. | | **400** | Malformed query or invalid variables. | | **401** | Missing or invalid authentication token. | | **403** | Authenticated but insufficient permissions for the requested resource. | | **429** | Rate limit exceeded. See [Rate Limits](rate-limits). | | **500** | Unexpected server error. Retry with exponential backoff. | ## Authentication errors An invalid or expired token produces a 401 response with an `UNAUTHENTICATED` error code. ```graphql query GetMyAccounts { me { accounts(first: 5) { nodes { id name } } } } ``` **Response:** ```json { "errors": [ { "message": "Unauthorized. Please provide a valid authentication token.", "extensions": { "code": "UNAUTHENTICATED" } } ], "data": null } ``` Common authentication issues: - **Expired session token** -- tokens from `createSessionUsingUserKey` expire after a period of inactivity. Re-authenticate to obtain a new token. - **Revoked API key** -- if a user key has been revoked, authentication will fail. Generate a new key. - **Wrong header** -- ensure the token is sent in the `x-auth-token` header (not `Authorization: Bearer`). ## Rate limiting When you exceed the rate limit you will receive an HTTP 429 response. Implement exponential backoff to handle this gracefully: ```typescript async function fetchWithRetry(url: string, options: RequestInit, maxRetries = 5): Promise { for (let attempt = 0; attempt < maxRetries; attempt++) { const response = await fetch(url, options); if (response.status === 429) { const delay = Math.min(1000 * Math.pow(2, attempt), 30000); console.warn(`Rate limited. Retrying in ${delay}ms...`); await new Promise(resolve => setTimeout(resolve, delay)); continue; } return response; } throw new Error("Max retries exceeded"); } ``` ## Troubleshooting common errors | Error | Cause | Solution | | ----------------------- | -------------------------------------------------- | -------------------------------------------------------- | | `UNAUTHENTICATED` | Missing or invalid token | Re-authenticate with `createSessionUsingUserKey` | | `FORBIDDEN` | Account lacks required feature or permission | Contact your account administrator | | `BAD_USER_INPUT` | Invalid variable values or missing required fields | Check variable types against the schema | | `INTERNAL_SERVER_ERROR` | Unexpected server issue | Retry after a brief delay; contact support if persistent |