CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-graphql-core

GraphQL-core is a Python port of GraphQL.js, the JavaScript reference implementation for GraphQL.

Pending
Overview
Eval results
Files

error-handling.mddocs/

Error Handling and Debugging

Handle GraphQL errors with location information, formatted error responses, and extensible error reporting. Provides comprehensive error management for GraphQL operations with detailed debugging information.

Capabilities

GraphQL Error Class

Primary error class for all GraphQL-related errors with location tracking and extensible metadata.

class GraphQLError(Exception):
    def __init__(
        self,
        message: str,
        nodes: Optional[Union[Node, Sequence[Node]]] = None,
        source: Optional[Source] = None,
        positions: Optional[Sequence[int]] = None,
        path: Optional[Sequence[Union[str, int]]] = None,
        original_error: Optional[Exception] = None,
        extensions: Optional[Dict[str, Any]] = None,
    )
    
    message: str
    locations: Optional[List[SourceLocation]]
    path: Optional[List[Union[str, int]]]
    nodes: Optional[List[Node]]
    source: Optional[Source]
    positions: Optional[List[int]]
    original_error: Optional[Exception]
    extensions: Optional[Dict[str, Any]]
    
    def formatted(self) -> GraphQLFormattedError
    def __str__(self) -> str

Properties:

  • message: Human-readable error description
  • locations: Source locations where error occurred
  • path: Path to the field that caused the error
  • nodes: AST nodes associated with the error
  • source: GraphQL source document
  • positions: Character positions in source
  • original_error: Underlying Python exception if any
  • extensions: Additional error metadata

Usage Examples

from graphql import GraphQLError, Source

# Basic error
error = GraphQLError("Something went wrong")
print(error.message)  # "Something went wrong"

# Error with location information
source = Source('query { user { name } }')
error_with_location = GraphQLError(
    "Field 'name' not found",
    source=source,
    positions=[15]
)
print(error_with_location.locations[0].line)    # Line number
print(error_with_location.locations[0].column)  # Column number

# Error with path information
path_error = GraphQLError(
    "Cannot resolve field",
    path=['user', 'profile', 'avatar']
)
print(path_error.path)  # ['user', 'profile', 'avatar']

# Error with extensions
extended_error = GraphQLError(
    "Access denied",
    extensions={'code': 'FORBIDDEN', 'userId': '123'}
)
print(extended_error.extensions)  # {'code': 'FORBIDDEN', 'userId': '123'}

Syntax Errors

Specialized error class for GraphQL syntax and parsing errors.

class GraphQLSyntaxError(GraphQLError):
    def __init__(
        self,
        source: Source,
        position: int,
        description: str,
    )

Usage Example

from graphql import parse, GraphQLSyntaxError

try:
    parse('query { user { name }')  # Missing closing brace
except GraphQLSyntaxError as e:
    print(f"Syntax error: {e.message}")
    print(f"At line {e.locations[0].line}, column {e.locations[0].column}")

Located Errors

Convert regular Python exceptions into GraphQL errors with location context.

def located_error(
    original_error: Exception,
    nodes: Optional[Union[Node, Sequence[Node]]] = None,
    path: Optional[Sequence[Union[str, int]]] = None,
) -> GraphQLError

Usage Example

from graphql import located_error

def risky_resolver(obj, info):
    try:
        return perform_database_operation()
    except DatabaseError as e:
        # Convert to GraphQL error with location
        raise located_error(e, info.field_nodes, info.path)

# Or in middleware
class ErrorHandlingMiddleware:
    def resolve(self, next_resolver, root, info, **args):
        try:
            return next_resolver(root, info, **args)
        except Exception as e:
            # Ensure all errors have location context
            if not isinstance(e, GraphQLError):
                e = located_error(e, info.field_nodes, info.path)
            raise e

Error Formatting

Format errors for client consumption with standardized structure.

# Error format types
GraphQLFormattedError = Dict[str, Any]
GraphQLErrorExtensions = Dict[str, Any]

# Format error manually
def format_error(error: GraphQLError) -> GraphQLFormattedError:
    formatted = {'message': error.message}
    
    if error.locations:
        formatted['locations'] = [
            {'line': loc.line, 'column': loc.column}
            for loc in error.locations
        ]
    
    if error.path:
        formatted['path'] = error.path
    
    if error.extensions:
        formatted['extensions'] = error.extensions
    
    return formatted

Usage Example

from graphql import GraphQLError

error = GraphQLError(
    "User not found",
    path=['user'],
    extensions={'code': 'NOT_FOUND', 'userId': '123'}
)

formatted = error.formatted()
print(formatted)
# {
#   'message': 'User not found',
#   'path': ['user'],
#   'extensions': {'code': 'NOT_FOUND', 'userId': '123'}
# }

Error Handling in Resolvers

Best practices for handling errors in resolver functions.

Resolver Error Patterns

from graphql import GraphQLError

# Return None for missing data (becomes null in response)
def resolve_optional_field(obj, info):
    user = get_user_by_id(obj.user_id)
    if not user:
        return None  # Field becomes null
    return user.name

# Raise GraphQLError for validation errors
def resolve_user(obj, info, id):
    if not id:
        raise GraphQLError("User ID is required")
    
    if not is_valid_id(id):
        raise GraphQLError(
            f"Invalid user ID: {id}",
            extensions={'code': 'INVALID_INPUT', 'field': 'id'}
        )
    
    user = find_user(id)
    if not user:
        raise GraphQLError(
            f"User not found: {id}",
            extensions={'code': 'NOT_FOUND', 'resourceType': 'User', 'id': id}
        )
    
    return user

# Handle authorization errors
def resolve_sensitive_field(obj, info):
    if not info.context.get('user'):
        raise GraphQLError(
            "Authentication required",
            extensions={'code': 'UNAUTHENTICATED'}
        )
    
    if not has_permission(info.context.user, 'read_sensitive_data'):
        raise GraphQLError(
            "Insufficient permissions",
            extensions={'code': 'FORBIDDEN', 'permission': 'read_sensitive_data'}
        )
    
    return obj.sensitive_data

# Wrap external service errors
def resolve_external_data(obj, info):
    try:
        return external_api.get_data(obj.id)
    except ExternalServiceError as e:
        raise GraphQLError(
            "External service unavailable",
            original_error=e,
            extensions={'code': 'SERVICE_UNAVAILABLE', 'service': 'external_api'}
        )
    except ValidationError as e:
        raise GraphQLError(
            f"Data validation failed: {e.message}",
            original_error=e,
            extensions={'code': 'DATA_VALIDATION_ERROR'}
        )

Error Aggregation

Handle multiple errors in execution results.

from graphql import ExecutionResult, GraphQLError

def create_execution_result_with_errors():
    errors = [
        GraphQLError("First error", path=['field1']),
        GraphQLError("Second error", path=['field2']),
    ]
    
    return ExecutionResult(
        data={'field1': None, 'field2': None, 'field3': 'success'},
        errors=errors
    )

# Error filtering and processing
def process_execution_result(result):
    if result.errors:
        # Categorize errors
        validation_errors = []
        auth_errors = []
        system_errors = []
        
        for error in result.errors:
            code = error.extensions.get('code') if error.extensions else None
            
            if code in ['INVALID_INPUT', 'DATA_VALIDATION_ERROR']:
                validation_errors.append(error)
            elif code in ['UNAUTHENTICATED', 'FORBIDDEN']:
                auth_errors.append(error)
            else:
                system_errors.append(error)
        
        # Handle different error types appropriately
        if auth_errors:
            # Return 401/403 HTTP status
            pass
        elif validation_errors and not system_errors:
            # Return 400 HTTP status
            pass
        elif system_errors:
            # Log and return 500 HTTP status
            pass

Custom Error Classes

Create domain-specific error classes for better error handling.

from graphql import GraphQLError

class ValidationError(GraphQLError):
    def __init__(self, message, field=None, value=None):
        super().__init__(
            message,
            extensions={
                'code': 'VALIDATION_ERROR',
                'field': field,
                'value': value
            }
        )

class AuthenticationError(GraphQLError):
    def __init__(self, message="Authentication required"):
        super().__init__(
            message,
            extensions={'code': 'UNAUTHENTICATED'}
        )

class AuthorizationError(GraphQLError):
    def __init__(self, message="Insufficient permissions", permission=None):
        super().__init__(
            message,
            extensions={
                'code': 'FORBIDDEN',
                'permission': permission
            }
        )

class NotFoundError(GraphQLError):
    def __init__(self, resource_type, resource_id):
        super().__init__(
            f"{resource_type} not found: {resource_id}",
            extensions={
                'code': 'NOT_FOUND',
                'resourceType': resource_type,
                'id': resource_id
            }
        )

# Usage in resolvers
def resolve_user(obj, info, id):
    if not info.context.get('user'):
        raise AuthenticationError()
    
    if not is_valid_id(id):
        raise ValidationError("Invalid ID format", field='id', value=id)
    
    user = find_user(id)
    if not user:
        raise NotFoundError('User', id)
    
    return user

Error Debugging

Debug and analyze GraphQL errors with detailed information.

from graphql import GraphQLError
import traceback
import logging

def debug_graphql_error(error: GraphQLError):
    print(f"GraphQL Error: {error.message}")
    
    if error.locations:
        for loc in error.locations:
            print(f"  Location: line {loc.line}, column {loc.column}")
    
    if error.path:
        print(f"  Path: {' -> '.join(map(str, error.path))}")
    
    if error.extensions:
        print(f"  Extensions: {error.extensions}")
    
    if error.original_error:
        print(f"  Original error: {error.original_error}")
        print(f"  Traceback:")
        traceback.print_exception(
            type(error.original_error),
            error.original_error,
            error.original_error.__traceback__
        )

# Logging middleware
class ErrorLoggingMiddleware:
    def __init__(self, logger=None):
        self.logger = logger or logging.getLogger(__name__)
    
    def resolve(self, next_resolver, root, info, **args):
        try:
            result = next_resolver(root, info, **args)
            return result
        except GraphQLError as e:
            # Log GraphQL errors with context
            self.logger.error(
                f"GraphQL error in {info.parent_type.name}.{info.field_name}: {e.message}",
                extra={
                    'field_path': info.path,
                    'operation': info.operation.operation.value,
                    'variables': info.variable_values,
                    'extensions': e.extensions
                }
            )
            raise
        except Exception as e:
            # Log and convert unexpected errors
            self.logger.exception(
                f"Unexpected error in {info.parent_type.name}.{info.field_name}",
                extra={'field_path': info.path}
            )
            raise located_error(e, info.field_nodes, info.path)

Types

# Import required types  
from typing import Any, Dict, List, Optional, Union, Sequence
from graphql.language import Node, Source, SourceLocation
from graphql.error import GraphQLError, GraphQLSyntaxError

# Error format types
GraphQLFormattedError = Dict[str, Any]
GraphQLErrorExtensions = Dict[str, Any]

# Core error classes
GraphQLError = class GraphQLError(Exception)
GraphQLSyntaxError = class GraphQLSyntaxError(GraphQLError)

# Error creation function
def located_error(
    original_error: Exception,
    nodes: Optional[Union[Node, Sequence[Node]]] = None,
    path: Optional[Sequence[Union[str, int]]] = None,
) -> GraphQLError

# Location information
class SourceLocation:
    line: int
    column: int

# Execution result with errors
class ExecutionResult:
    data: Optional[Dict[str, Any]]
    errors: Optional[List[GraphQLError]]
    extensions: Optional[Dict[str, Any]]

Install with Tessl CLI

npx tessl i tessl/pypi-graphql-core

docs

error-handling.md

execution-engine.md

execution.md

index.md

language.md

type-system.md

utilities.md

validation.md

tile.json