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

# Error Handling

> GraphQL error codes and handling

GraphQL errors follow the standard GraphQL error format with additional `extensions` for error codes.

## Error Format

```json theme={null}
{
  "errors": [
    {
      "message": "Human-readable error message",
      "extensions": {
        "code": "ERROR_CODE",
        "additionalInfo": "..."
      },
      "path": ["queryName", "fieldName"]
    }
  ],
  "data": {
    "queryName": null
  }
}
```

## Common Error Codes

| Code | HTTP Status | Description |
| - | - | - |
| `NOT_FOUND` | 200 | Saved search or package not found |
| `UNAUTHORIZED` | 200 | Invalid or missing API key |
| `FORBIDDEN` | 200 | API key inactive or its owner suspended |
| `ORGANIZATION_SUSPENDED` | 200 | The API key's organization is suspended |
| `INSUFFICIENT_SCOPE` | 200 | API key lacks the scope for a requested field |
| `BAD_USER_INPUT` | 400 | Missing or invalid variables |
| `GRAPHQL_VALIDATION_FAILED` | 400 | Query does not match the schema |
| `GRAPHQL_PARSE_FAILED` | 400 | Query is not valid GraphQL syntax |
| `INTERNAL_ERROR` | 200 or 500 | Server error |

## Preview-Specific Error Codes

| Code | Description |
| - | - |
| `PREVIEW_NOT_FOUND` | No preview link exists for the token |
| `COMPONENT_NOT_FOUND` | Previewed component, or the requested version, not found |
| `CONTENT_NOT_FOUND` | Previewed content not found |
| `UNAUTHORIZED_PACKAGE_ACCESS` | Package is not part of the preview |

## Error Examples

### Resource Not Found

`content`, `component`, and `tag` return `null` without an error when the ID doesn't exist:

```json theme={null}
{
  "data": {
    "content": null
  }
}
```

### Unauthorized

```json theme={null}
{
  "errors": [
    {
      "message": "Unauthorized",
      "extensions": {
        "code": "UNAUTHORIZED"
      },
      "path": ["contents"]
    }
  ],
  "data": null
}
```

### Invalid Preview Token

```json theme={null}
{
  "errors": [
    {
      "message": "Preview link not found",
      "extensions": {
        "code": "PREVIEW_NOT_FOUND"
      },
      "path": ["preview"]
    }
  ],
  "data": { "preview": null }
}
```

### Version Not Found

```json theme={null}
{
  "errors": [
    {
      "message": "Component not found",
      "extensions": {
        "code": "COMPONENT_NOT_FOUND"
      },
      "path": ["preview"]
    }
  ],
  "data": { "preview": null }
}
```

### Validation Error

```json theme={null}
{
  "errors": [
    {
      "message": "Variable \"$id\" of required type \"ID!\" was not provided.",
      "extensions": {
        "code": "BAD_USER_INPUT"
      },
      "locations": [{ "line": 1, "column": 7 }]
    }
  ]
}
```

## Client-Side Error Handling

### JavaScript

```javascript theme={null}
async function fetchContent(id) {
  const response = await fetch('https://api.metabind.ai/graphql', {
    method: 'POST',
    headers: {
      'x-api-key': API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      query: GET_CONTENT_QUERY,
      variables: { id }
    })
  });

  const { data, errors } = await response.json();

  if (errors) {
    for (const error of errors) {
      switch (error.extensions?.code) {
        case 'UNAUTHORIZED':
          throw new Error('Invalid API key');
        default:
          console.error('GraphQL error:', error.message);
      }
    }
  }

  // content returns null, not an error, when the ID doesn't exist
  if (data?.content === null) {
    console.warn(`Content ${id} not found`);
    return null;
  }

  return data?.content;
}
```

### Apollo Client

```javascript theme={null}
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client';
import { onError } from '@apollo/client/link/error';

const errorLink = onError(({ graphQLErrors, networkError }) => {
  if (graphQLErrors) {
    for (const { message, extensions, path } of graphQLErrors) {
      switch (extensions?.code) {
        case 'UNAUTHORIZED':
          // Redirect to login or refresh API key
          handleUnauthorized();
          break;
        case 'NOT_FOUND':
          console.warn(`Resource not found at ${path?.join('.')}`);
          break;
        case 'PREVIEW_NOT_FOUND':
          // Preview link no longer exists
          handleExpiredPreview();
          break;
        default:
          console.error(`GraphQL error: ${message}`);
      }
    }
  }

  if (networkError) {
    console.error(`Network error: ${networkError}`);
  }
});

const client = new ApolloClient({
  link: errorLink.concat(httpLink),
  cache: new InMemoryCache()
});
```

### React Hook

```javascript theme={null}
function useContent(id) {
  const { data, loading, error } = useQuery(GET_CONTENT, {
    variables: { id },
    errorPolicy: 'all'  // Return partial data with errors
  });

  if (error) {
    throw error;  // Re-throw unexpected errors
  }

  // content returns null, not an error, when the ID doesn't exist
  if (data?.content === null) {
    return { content: null, loading: false, notFound: true };
  }

  return { content: data?.content, loading, notFound: false };
}
```

## Subscription Error Handling

```javascript theme={null}
import { createClient } from 'graphql-ws';

const client = createClient({
  url: 'wss://ws-api.metabind.ai?protocol=graphql-transport-ws',
  connectionParams: {
    apiKey: API_KEY
  },
  on: {
    error: (error) => {
      console.error('WebSocket error:', error);
    },
    closed: () => {
      console.log('WebSocket closed');
    }
  },
  retryAttempts: 5,
  shouldRetry: () => true
});

client.subscribe({
  query: CONTENT_UPDATED,
  variables: { id: 'cont123' }
}, {
  next: (data) => {
    if (data.errors) {
      for (const error of data.errors) {
        console.error('Subscription error:', error.message);
      }
      return;
    }
    handleUpdate(data.data.contentUpdated);
  },
  error: (err) => {
    console.error('Subscription error:', err);
  },
  complete: () => {
    console.log('Subscription complete');
  }
});
```

## Best Practices

1. **Always check for errors**: GraphQL can return partial data with errors
2. **Use error codes**: Check `extensions.code` for programmatic error handling
3. **Handle `null` results**: `content`, `component`, and `tag` return `null` when a resource is deleted, unpublished, or doesn't exist
4. **Implement retry logic**: Network errors may be transient
5. **Log errors**: Include path and extensions for debugging
6. **Differentiate preview errors**: Preview links stop working when they are deleted; handle `PREVIEW_NOT_FOUND` separately from API key errors


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