Skip to main content

Handling Errors and Response Codes

Understanding how Common Ground APIs handle errors and response codes is crucial for building robust applications. This guide explains the GraphQL error model, HTTP status codes, and best practices for error handling.

GraphQL Error Model

GraphQL Returns 200 Status Code

Unlike REST APIs, GraphQL typically returns HTTP status code 200 OK even when errors occur. Errors are included in the response body within an errors array, while successful data appears in the data field.

This is a fundamental aspect of GraphQL's design: the HTTP status code indicates whether the request was successfully received and processed by the server, not whether the operation succeeded. The response body tells you the actual result.

Response Structure

A GraphQL response always has this structure:

{
"data": { ... },
"errors": [ ... ]
}

Successful Response:

{
"data": {
"items": [
{ "_id": "123", "title": "Item 1" }
]
}
}

Response with Errors:

{
"data": null,
"errors": [
{
"message": "Authentication error",
"extensions": {
"code": "UNAUTHENTICATED"
},
"path": ["items"]
}
]
}

Partial Success (Some Fields Failed):

{
"data": {
"items": [
{ "_id": "123", "title": "Item 1" }
],
"user": null
},
"errors": [
{
"message": "User not found",
"path": ["user"]
}
]
}

HTTP Status Codes

While GraphQL operations typically return 200 OK, certain HTTP status codes are used for specific scenarios:

200 OK

When it's used:

  • Successful GraphQL queries and mutations
  • GraphQL operations with errors (errors are in the response body)

What to check:

  • Always inspect the errors array in the response body
  • Check the data field for successful results
  • Even with 200 OK, the operation may have failed

400 Bad Request

When it's used:

  • Invalid GraphQL query syntax
  • Malformed request body
  • Missing required headers
  • Invalid JSON in request body
  • Request body too large

What it means: The request itself is invalid and cannot be processed. This typically happens before the GraphQL query is executed.

Common causes:

  • Syntax errors in GraphQL query
  • Missing or invalid Content-Type: application/json header
  • Invalid JSON structure
  • Request exceeds size limits

Example:

// Invalid GraphQL syntax
const query = `
query {
items { // Missing closing brace
`;

// This will return 400 Bad Request

How to handle:

  • Validate your GraphQL queries before sending
  • Ensure proper JSON formatting
  • Check request headers
  • Verify request body size

401 Unauthorized

When it's used:

  • Missing authentication credentials
  • Invalid authentication token
  • Expired authentication token

In GraphQL, authentication errors are often returned as 200 OK with an error in the response body. However, if authentication fails at the HTTP middleware level (before the GraphQL layer), you may receive 401.

404 Not Found

When it's used:

  • Requesting a non-existent endpoint (not /graphql)
  • Invalid API route

This is rare for GraphQL endpoints. If you're hitting /graphql, you should get 200 OK even with errors.

429 Too Many Requests

When it's used:

  • Rate limit exceeded
  • Too many requests in a time window

What to do:

  • Implement exponential backoff
  • Respect rate limit headers if provided
  • Reduce request frequency
  • Check your rate limit tier

500 Internal Server Error

When it's used:

  • Server-side errors that prevent request processing
  • Database connection failures
  • Unexpected server crashes
  • Errors in server middleware (before GraphQL execution)

Most GraphQL errors are returned as 200 OK with error details. 500 typically indicates a problem before GraphQL execution.

How to handle:

  • Retry with exponential backoff
  • Log the error for investigation
  • Contact support if persistent

GraphQL Error Types

Error Structure

Each error in the errors array has this structure:

{
message: string; // Human-readable error message
locations?: Array<{ // Where in the query the error occurred
line: number;
column: number;
}>;
path?: Array<string | number>; // Path to the field that errored
extensions?: { // Additional error information
code: string; // Error code (e.g., "UNAUTHENTICATED")
[key: string]: any; // Additional custom fields
};
}

Common Error Codes

UNAUTHENTICATED

Code: UNAUTHENTICATED

When it occurs:

  • No user session found
  • Missing authentication context
  • Invalid or expired session

HTTP Status: Usually 200 OK (error in response body)

Example:

{
"data": null,
"errors": [
{
"message": "Authentication error",
"extensions": {
"code": "UNAUTHENTICATED"
}
}
]
}

How to handle:

  • Check if user is logged in
  • Refresh authentication token
  • Redirect to login page
  • Retry request after authentication

UNAUTHORIZED

Code: UNAUTHORIZED

When it occurs:

  • User is authenticated but lacks required permissions
  • Insufficient role or access level
  • Operation not allowed for current user

HTTP Status: Usually 200 OK (error in response body)

Example:

{
"data": null,
"errors": [
{
"message": "Unauthorized",
"extensions": {
"code": "UNAUTHORIZED"
}
}
]
}

How to handle:

  • Check user permissions
  • Verify required role/access level
  • Show appropriate error message to user
  • Hide UI elements that require higher permissions

Validation Errors

Code: Various (often no specific code)

When it occurs:

  • Invalid input values
  • Missing required fields
  • Field value constraints violated
  • Type mismatches

HTTP Status: Usually 200 OK (error in response body)

Example:

{
"data": null,
"errors": [
{
"message": "Invalid pagination: page must be greater than 0",
"path": ["items"],
"extensions": {
"code": "BAD_USER_INPUT"
}
}
]
}

How to handle:

  • Validate input on client side before sending
  • Display validation errors to users
  • Highlight invalid fields in forms
  • Provide clear error messages

Not Found Errors

Code: Various (often no specific code)

When it occurs:

  • Requested resource doesn't exist
  • Invalid ID provided
  • Resource was deleted

HTTP Status: Usually 200 OK (error in response body)

Example:

{
"data": {
"item": null
},
"errors": [
{
"message": "Item not found",
"path": ["item"]
}
]
}

How to handle:

  • Check if resource exists before displaying
  • Show "not found" message to user
  • Redirect to appropriate page
  • Handle gracefully in UI

Best Practices

  • Always check for errors - Even when you receive 200 OK, check the errors array in the response body
  • Check HTTP status before parsing - Validate HTTP status before attempting to parse JSON to catch network-level errors
  • Handle partial success - GraphQL allows partial data. Some fields may succeed while others fail. Check both data and errors in responses
  • Differentiate error types - Handle errors based on extensions.code (e.g., UNAUTHENTICATED, UNAUTHORIZED, BAD_USER_INPUT) to provide appropriate user feedback
  • Implement retry logic - For transient errors (network issues, rate limits), implement retry logic with exponential backoff
  • Log errors appropriately - Log different error types with appropriate detail levels for debugging
  • Provide user-friendly messages - Transform technical error messages into user-friendly ones for end users

Error Handling Patterns

Try-Catch with Status Check

Wrap GraphQL requests in a try-catch block to handle both HTTP-level and GraphQL-level errors. Check the HTTP response status using response.ok to catch network errors, rate limits, and authentication failures that occur before GraphQL execution. Then parse the JSON response and check for GraphQL errors in the errors array. Handle GraphQL errors and network errors separately—GraphQL errors contain detailed error information in the response body, while network errors indicate connection or HTTP-level issues.

Apollo Client Error Handling

Apollo Client provides an error link that automatically intercepts and handles errors. Use the onError function to create an error link that receives graphQLErrors and networkError separately. GraphQL errors include error codes in extensions.code (such as UNAUTHENTICATED or UNAUTHORIZED) that you can use to handle specific error types. Network errors include HTTP status codes (like 429 for rate limits) that you can check to implement appropriate handling. Add the error link to your Apollo Client configuration using from([errorLink, httpLink]) to enable automatic error handling across all requests.

Summary

Key Takeaways

  • GraphQL returns 200 OK even with errors - Always check the errors array in the response body
  • 400-range HTTP errors indicate invalid requests (malformed queries, missing headers, etc.)
  • GraphQL errors are in the response body - Check result.errors even when HTTP status is 200
  • Partial success is possible - Some fields may succeed while others fail
  • Error codes in extensions - Use extensions.code to identify error types
  • Differentiate error types - Handle authentication, authorization, validation, and server errors differently

Quick Reference

HTTP StatusMeaningWhen to Check
200 OKRequest processedAlways check errors array
400 Bad RequestInvalid requestBefore sending (validate query)
401 UnauthorizedMissing/invalid authCheck authentication
429 Too Many RequestsRate limit exceededImplement backoff
500 Internal Server ErrorServer errorRetry with backoff

Common Error Codes

  • UNAUTHENTICATED - User not logged in
  • UNAUTHORIZED - Insufficient permissions
  • BAD_USER_INPUT - Invalid input values
  • (No code) - Generic errors, check message