or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

caching.mdcore-client.mderror-handling.mdindex.mdlink-system.mdmasking.mdreact-hooks.mdtesting.mdutilities.md
tile.json

error-handling.mddocs/

Error Handling

Comprehensive error handling for GraphQL operations and transport failures. Apollo Client provides structured error types, handling mechanisms, and policies for managing GraphQL errors, network failures, and protocol-level issues.

Capabilities

ApolloError

Main error class that combines GraphQL errors, network errors, and client errors.

/**
 * Main error class for Apollo Client operations
 * Combines GraphQL errors, network errors, and client-side errors
 */
class ApolloError extends Error {
  /** Human-readable error message */
  message: string;
  
  /** GraphQL execution errors */
  graphQLErrors: readonly GraphQLError[];
  
  /** Protocol-level errors */
  protocolErrors: readonly GraphQLError[];
  
  /** Client-side errors */
  clientErrors: readonly Error[];
  
  /** Network transport error */
  networkError: Error | null;
  
  /** Additional error information */
  extraInfo: any;
  
  constructor(options: ErrorOptions);
}

interface ErrorOptions {
  graphQLErrors?: readonly GraphQLError[];
  protocolErrors?: readonly GraphQLError[];
  clientErrors?: readonly Error[];
  networkError?: Error | null;
  extraInfo?: any;
}

Usage Example:

import { ApolloError } from "@apollo/client/errors";

function handleApolloError(error: ApolloError) {
  console.log('Error message:', error.message);
  
  // Handle GraphQL errors
  if (error.graphQLErrors.length > 0) {
    error.graphQLErrors.forEach(({ message, locations, path, extensions }) => {
      console.log(`GraphQL error: ${message}`);
      console.log('Location:', locations);
      console.log('Path:', path);
      console.log('Extensions:', extensions);
    });
  }
  
  // Handle network errors
  if (error.networkError) {
    console.log('Network error:', error.networkError.message);
    
    if (error.networkError instanceof ServerError) {
      console.log('Status code:', error.networkError.statusCode);
      console.log('Response:', error.networkError.result);
    }
  }
  
  // Handle protocol errors
  if (error.protocolErrors.length > 0) {
    console.log('Protocol errors:', error.protocolErrors);
  }
}

Server Errors

Errors related to HTTP server responses and parsing.

/**
 * HTTP server error with response details
 * Thrown when server returns non-2xx status codes
 */
class ServerError extends Error {
  /** HTTP response object */
  response: Response;
  
  /** Parsed response body */
  result: Record<string, any>;
  
  /** HTTP status code */
  statusCode: number;
  
  constructor(response: Response, result: any, message?: string);
}

/**
 * JSON parsing error for server responses
 * Thrown when server response cannot be parsed as JSON
 */
class ServerParseError extends Error {
  /** HTTP response object */
  response: Response;
  
  /** Raw response body text */
  bodyText: string;
  
  /** HTTP status code */
  statusCode: number;
  
  constructor(response: Response, bodyText: string, message?: string);
}

Usage Example:

import { ServerError, ServerParseError } from "@apollo/client/errors";

// In error handling
if (error.networkError instanceof ServerError) {
  switch (error.networkError.statusCode) {
    case 401:
      // Handle unauthorized
      redirectToLogin();
      break;
    case 403:
      // Handle forbidden
      showAccessDeniedMessage();
      break;
    case 500:
      // Handle server error
      showServerErrorMessage();
      break;
    default:
      console.log('Server error:', error.networkError.result);
  }
}

if (error.networkError instanceof ServerParseError) {
  console.log('Failed to parse server response');
  console.log('Raw response:', error.networkError.bodyText);
}

Link Errors

Errors specific to the link layer and transport.

/**
 * Error thrown by links in the link chain
 * Used for transport-specific and middleware errors
 */
class LinkError extends Error {
  constructor(message?: string, link?: ApolloLink);
}

/**
 * Register a link-specific error for debugging
 * @param error - Error to register
 * @param link - Link that threw the error
 */
function registerLinkError(error: Error, link?: ApolloLink): void;

/**
 * Local state resolver error
 * Thrown when local state resolvers fail
 */
class LocalStateError extends Error {
  constructor(message: string);
}

Error Utilities

Utility functions for error detection and handling.

/**
 * Check if value is error-like (has message property)
 * @param value - Value to check
 * @returns True if value is error-like
 */
function isErrorLike(value: any): value is { message: string };

/**
 * Convert any value to error-like object
 * @param error - Value to convert
 * @returns Error-like object
 */
function toErrorLike(error: unknown): { message: string };

/**
 * Check if GraphQL result has protocol errors
 * @param result - GraphQL result to check
 * @returns True if result has protocol errors
 */
function graphQLResultHasProtocolErrors<T extends {}>(
  result: T
): result is T & { extensions: Record<string | symbol, any> };

/**
 * Symbol for protocol errors in result extensions
 */
const PROTOCOL_ERRORS_SYMBOL: unique symbol;

Error Policies

Configuration options for how to handle different types of errors.

/**
 * Error policy for GraphQL operations
 * Controls how GraphQL errors are handled and exposed
 */
type ErrorPolicy = 'none' | 'ignore' | 'all';

// none: Treat GraphQL errors as runtime errors (default)
// ignore: Ignore GraphQL errors and return partial data
// all: Return both data and errors

Usage Example:

// Different error policies in action
const { data, error } = useQuery(GET_USER, {
  variables: { id: '1' },
  errorPolicy: 'all' // Return both data and errors
});

// With 'all' policy, you might get partial data even with errors
if (data) {
  // Render what data you have
  renderUser(data.user);
}

if (error) {
  // Handle errors separately
  handleErrors(error.graphQLErrors);
}

// Global error policy configuration
const client = new ApolloClient({
  // ... other options
  defaultOptions: {
    query: {
      errorPolicy: 'ignore' // Ignore errors globally for queries
    },
    mutate: {
      errorPolicy: 'none' // Treat errors as exceptions for mutations
    }
  }
});

Error Link Integration

Using ErrorLink for centralized error handling.

import { ErrorLink } from "@apollo/client/link/error";

/**
 * Error handler function for ErrorLink
 * @param errorResponse - Error response with context
 * @returns Observable to retry or void to continue
 */
type ErrorHandler = (errorResponse: ErrorResponse) => Observable<FetchResult> | void;

interface ErrorResponse {
  /** GraphQL errors */
  graphQLErrors?: readonly GraphQLError[];
  
  /** Network error */
  networkError?: Error;
  
  /** Current operation being executed */
  operation: Operation;
  
  /** Forward function to retry operation */
  forward: NextLink;
  
  /** Response object (if available) */
  response?: ExecutionResult;
}

Usage Example:

import { ErrorLink } from "@apollo/client/link/error";
import { fromPromise } from "@apollo/client/link";

const errorLink = new ErrorLink(({ graphQLErrors, networkError, operation, forward }) => {
  if (graphQLErrors) {
    graphQLErrors.forEach(({ message, locations, path, extensions }) => {
      console.log(`GraphQL error: Message: ${message}, Location: ${locations}, Path: ${path}`);
      
      // Handle specific error codes
      if (extensions?.code === 'UNAUTHENTICATED') {
        // Clear stored tokens
        localStorage.removeItem('token');
        // Redirect to login
        window.location.href = '/login';
      }
    });
  }
  
  if (networkError) {
    console.log(`Network error: ${networkError}`);
    
    // Retry logic for network errors
    if (networkError.statusCode === 401) {
      // Token might be expired, try to refresh
      return fromPromise(
        refreshAuthToken().then(() => {
          // Retry the operation with new token
          return forward(operation);
        }).catch(() => {
          // Refresh failed, redirect to login
          window.location.href = '/login';
          return;
        })
      );
    }
  }
});

// Use in link chain
const link = from([
  errorLink,
  httpLink
]);

Combined Error Types

Advanced error combination classes for complex error scenarios.

/**
 * Combined GraphQL errors from multiple sources
 * Aggregates multiple GraphQL errors into single error
 */
class CombinedGraphQLErrors extends Error {
  constructor(errors: readonly GraphQLError[]);
  
  /** Check if errors object is CombinedGraphQLErrors */
  static is(errors: any): errors is CombinedGraphQLErrors;
}

/**
 * Combined protocol errors from transport layer
 * Aggregates protocol-level errors
 */
class CombinedProtocolErrors extends Error {
  constructor(errors: readonly GraphQLError[]);
  
  /** Check if errors object is CombinedProtocolErrors */
  static is(errors: any): errors is CombinedProtocolErrors;
}

/**
 * Wrapper for non-standard error types
 * Ensures all errors have consistent interface
 */
class UnconventionalError extends Error {
  constructor(error: unknown);
}

Usage Example:

// Handling combined errors
function handleCombinedErrors(error: ApolloError) {
  if (CombinedGraphQLErrors.is(error.graphQLErrors)) {
    console.log('Multiple GraphQL errors occurred');
    error.graphQLErrors.forEach(err => {
      console.log('Individual error:', err.message);
    });
  }
  
  if (error.protocolErrors && CombinedProtocolErrors.is(error.protocolErrors)) {
    console.log('Multiple protocol errors occurred');
    // Handle protocol-specific error recovery
  }
}

Common Error Patterns

// Comprehensive error handling in components
function MyComponent() {
  const { data, loading, error } = useQuery(GET_DATA, {
    errorPolicy: 'all',
    onError: (error) => {
      // Component-level error handling
      if (error.networkError) {
        showToast('Network error occurred', 'error');
      }
      
      if (error.graphQLErrors.length > 0) {
        const hasAuthError = error.graphQLErrors.some(
          err => err.extensions?.code === 'UNAUTHENTICATED'
        );
        
        if (hasAuthError) {
          redirectToLogin();
        }
      }
    }
  });

  if (loading) return <Spinner />;
  
  // With errorPolicy: 'all', we might have both data and errors
  if (error && !data) {
    return <ErrorDisplay error={error} />;
  }
  
  return (
    <div>
      {data && <DataDisplay data={data} />}
      {error && <ErrorBanner errors={error.graphQLErrors} />}
    </div>
  );
}

Types

interface GraphQLError {
  message: string;
  locations?: readonly SourceLocation[];
  path?: readonly (string | number)[];
  nodes?: readonly ASTNode[];
  source?: Source;
  positions?: readonly number[];
  originalError?: Error;
  extensions?: Record<string, any>;
}

interface ExecutionResult<TData = Record<string, any>, TExtensions = Record<string, any>> {
  errors?: readonly GraphQLError[];
  data?: TData | null;
  extensions?: TExtensions;
}

type NextLink = (operation: Operation) => Observable<FetchResult>;