0
# Error Handling and Debugging
1
2
Handle GraphQL errors with location information, formatted error responses, and extensible error reporting. Provides comprehensive error management for GraphQL operations with detailed debugging information.
3
4
## Capabilities
5
6
### GraphQL Error Class
7
8
Primary error class for all GraphQL-related errors with location tracking and extensible metadata.
9
10
```python { .api }
11
class GraphQLError(Exception):
12
def __init__(
13
self,
14
message: str,
15
nodes: Optional[Union[Node, Sequence[Node]]] = None,
16
source: Optional[Source] = None,
17
positions: Optional[Sequence[int]] = None,
18
path: Optional[Sequence[Union[str, int]]] = None,
19
original_error: Optional[Exception] = None,
20
extensions: Optional[Dict[str, Any]] = None,
21
)
22
23
message: str
24
locations: Optional[List[SourceLocation]]
25
path: Optional[List[Union[str, int]]]
26
nodes: Optional[List[Node]]
27
source: Optional[Source]
28
positions: Optional[List[int]]
29
original_error: Optional[Exception]
30
extensions: Optional[Dict[str, Any]]
31
32
def formatted(self) -> GraphQLFormattedError
33
def __str__(self) -> str
34
```
35
36
**Properties:**
37
- `message`: Human-readable error description
38
- `locations`: Source locations where error occurred
39
- `path`: Path to the field that caused the error
40
- `nodes`: AST nodes associated with the error
41
- `source`: GraphQL source document
42
- `positions`: Character positions in source
43
- `original_error`: Underlying Python exception if any
44
- `extensions`: Additional error metadata
45
46
#### Usage Examples
47
48
```python
49
from graphql import GraphQLError, Source
50
51
# Basic error
52
error = GraphQLError("Something went wrong")
53
print(error.message) # "Something went wrong"
54
55
# Error with location information
56
source = Source('query { user { name } }')
57
error_with_location = GraphQLError(
58
"Field 'name' not found",
59
source=source,
60
positions=[15]
61
)
62
print(error_with_location.locations[0].line) # Line number
63
print(error_with_location.locations[0].column) # Column number
64
65
# Error with path information
66
path_error = GraphQLError(
67
"Cannot resolve field",
68
path=['user', 'profile', 'avatar']
69
)
70
print(path_error.path) # ['user', 'profile', 'avatar']
71
72
# Error with extensions
73
extended_error = GraphQLError(
74
"Access denied",
75
extensions={'code': 'FORBIDDEN', 'userId': '123'}
76
)
77
print(extended_error.extensions) # {'code': 'FORBIDDEN', 'userId': '123'}
78
```
79
80
### Syntax Errors
81
82
Specialized error class for GraphQL syntax and parsing errors.
83
84
```python { .api }
85
class GraphQLSyntaxError(GraphQLError):
86
def __init__(
87
self,
88
source: Source,
89
position: int,
90
description: str,
91
)
92
```
93
94
#### Usage Example
95
96
```python
97
from graphql import parse, GraphQLSyntaxError
98
99
try:
100
parse('query { user { name }') # Missing closing brace
101
except GraphQLSyntaxError as e:
102
print(f"Syntax error: {e.message}")
103
print(f"At line {e.locations[0].line}, column {e.locations[0].column}")
104
```
105
106
### Located Errors
107
108
Convert regular Python exceptions into GraphQL errors with location context.
109
110
```python { .api }
111
def located_error(
112
original_error: Exception,
113
nodes: Optional[Union[Node, Sequence[Node]]] = None,
114
path: Optional[Sequence[Union[str, int]]] = None,
115
) -> GraphQLError
116
```
117
118
#### Usage Example
119
120
```python
121
from graphql import located_error
122
123
def risky_resolver(obj, info):
124
try:
125
return perform_database_operation()
126
except DatabaseError as e:
127
# Convert to GraphQL error with location
128
raise located_error(e, info.field_nodes, info.path)
129
130
# Or in middleware
131
class ErrorHandlingMiddleware:
132
def resolve(self, next_resolver, root, info, **args):
133
try:
134
return next_resolver(root, info, **args)
135
except Exception as e:
136
# Ensure all errors have location context
137
if not isinstance(e, GraphQLError):
138
e = located_error(e, info.field_nodes, info.path)
139
raise e
140
```
141
142
### Error Formatting
143
144
Format errors for client consumption with standardized structure.
145
146
```python { .api }
147
# Error format types
148
GraphQLFormattedError = Dict[str, Any]
149
GraphQLErrorExtensions = Dict[str, Any]
150
151
# Format error manually
152
def format_error(error: GraphQLError) -> GraphQLFormattedError:
153
formatted = {'message': error.message}
154
155
if error.locations:
156
formatted['locations'] = [
157
{'line': loc.line, 'column': loc.column}
158
for loc in error.locations
159
]
160
161
if error.path:
162
formatted['path'] = error.path
163
164
if error.extensions:
165
formatted['extensions'] = error.extensions
166
167
return formatted
168
```
169
170
#### Usage Example
171
172
```python
173
from graphql import GraphQLError
174
175
error = GraphQLError(
176
"User not found",
177
path=['user'],
178
extensions={'code': 'NOT_FOUND', 'userId': '123'}
179
)
180
181
formatted = error.formatted()
182
print(formatted)
183
# {
184
# 'message': 'User not found',
185
# 'path': ['user'],
186
# 'extensions': {'code': 'NOT_FOUND', 'userId': '123'}
187
# }
188
```
189
190
### Error Handling in Resolvers
191
192
Best practices for handling errors in resolver functions.
193
194
#### Resolver Error Patterns
195
196
```python
197
from graphql import GraphQLError
198
199
# Return None for missing data (becomes null in response)
200
def resolve_optional_field(obj, info):
201
user = get_user_by_id(obj.user_id)
202
if not user:
203
return None # Field becomes null
204
return user.name
205
206
# Raise GraphQLError for validation errors
207
def resolve_user(obj, info, id):
208
if not id:
209
raise GraphQLError("User ID is required")
210
211
if not is_valid_id(id):
212
raise GraphQLError(
213
f"Invalid user ID: {id}",
214
extensions={'code': 'INVALID_INPUT', 'field': 'id'}
215
)
216
217
user = find_user(id)
218
if not user:
219
raise GraphQLError(
220
f"User not found: {id}",
221
extensions={'code': 'NOT_FOUND', 'resourceType': 'User', 'id': id}
222
)
223
224
return user
225
226
# Handle authorization errors
227
def resolve_sensitive_field(obj, info):
228
if not info.context.get('user'):
229
raise GraphQLError(
230
"Authentication required",
231
extensions={'code': 'UNAUTHENTICATED'}
232
)
233
234
if not has_permission(info.context.user, 'read_sensitive_data'):
235
raise GraphQLError(
236
"Insufficient permissions",
237
extensions={'code': 'FORBIDDEN', 'permission': 'read_sensitive_data'}
238
)
239
240
return obj.sensitive_data
241
242
# Wrap external service errors
243
def resolve_external_data(obj, info):
244
try:
245
return external_api.get_data(obj.id)
246
except ExternalServiceError as e:
247
raise GraphQLError(
248
"External service unavailable",
249
original_error=e,
250
extensions={'code': 'SERVICE_UNAVAILABLE', 'service': 'external_api'}
251
)
252
except ValidationError as e:
253
raise GraphQLError(
254
f"Data validation failed: {e.message}",
255
original_error=e,
256
extensions={'code': 'DATA_VALIDATION_ERROR'}
257
)
258
```
259
260
### Error Aggregation
261
262
Handle multiple errors in execution results.
263
264
```python
265
from graphql import ExecutionResult, GraphQLError
266
267
def create_execution_result_with_errors():
268
errors = [
269
GraphQLError("First error", path=['field1']),
270
GraphQLError("Second error", path=['field2']),
271
]
272
273
return ExecutionResult(
274
data={'field1': None, 'field2': None, 'field3': 'success'},
275
errors=errors
276
)
277
278
# Error filtering and processing
279
def process_execution_result(result):
280
if result.errors:
281
# Categorize errors
282
validation_errors = []
283
auth_errors = []
284
system_errors = []
285
286
for error in result.errors:
287
code = error.extensions.get('code') if error.extensions else None
288
289
if code in ['INVALID_INPUT', 'DATA_VALIDATION_ERROR']:
290
validation_errors.append(error)
291
elif code in ['UNAUTHENTICATED', 'FORBIDDEN']:
292
auth_errors.append(error)
293
else:
294
system_errors.append(error)
295
296
# Handle different error types appropriately
297
if auth_errors:
298
# Return 401/403 HTTP status
299
pass
300
elif validation_errors and not system_errors:
301
# Return 400 HTTP status
302
pass
303
elif system_errors:
304
# Log and return 500 HTTP status
305
pass
306
```
307
308
### Custom Error Classes
309
310
Create domain-specific error classes for better error handling.
311
312
```python
313
from graphql import GraphQLError
314
315
class ValidationError(GraphQLError):
316
def __init__(self, message, field=None, value=None):
317
super().__init__(
318
message,
319
extensions={
320
'code': 'VALIDATION_ERROR',
321
'field': field,
322
'value': value
323
}
324
)
325
326
class AuthenticationError(GraphQLError):
327
def __init__(self, message="Authentication required"):
328
super().__init__(
329
message,
330
extensions={'code': 'UNAUTHENTICATED'}
331
)
332
333
class AuthorizationError(GraphQLError):
334
def __init__(self, message="Insufficient permissions", permission=None):
335
super().__init__(
336
message,
337
extensions={
338
'code': 'FORBIDDEN',
339
'permission': permission
340
}
341
)
342
343
class NotFoundError(GraphQLError):
344
def __init__(self, resource_type, resource_id):
345
super().__init__(
346
f"{resource_type} not found: {resource_id}",
347
extensions={
348
'code': 'NOT_FOUND',
349
'resourceType': resource_type,
350
'id': resource_id
351
}
352
)
353
354
# Usage in resolvers
355
def resolve_user(obj, info, id):
356
if not info.context.get('user'):
357
raise AuthenticationError()
358
359
if not is_valid_id(id):
360
raise ValidationError("Invalid ID format", field='id', value=id)
361
362
user = find_user(id)
363
if not user:
364
raise NotFoundError('User', id)
365
366
return user
367
```
368
369
### Error Debugging
370
371
Debug and analyze GraphQL errors with detailed information.
372
373
```python
374
from graphql import GraphQLError
375
import traceback
376
import logging
377
378
def debug_graphql_error(error: GraphQLError):
379
print(f"GraphQL Error: {error.message}")
380
381
if error.locations:
382
for loc in error.locations:
383
print(f" Location: line {loc.line}, column {loc.column}")
384
385
if error.path:
386
print(f" Path: {' -> '.join(map(str, error.path))}")
387
388
if error.extensions:
389
print(f" Extensions: {error.extensions}")
390
391
if error.original_error:
392
print(f" Original error: {error.original_error}")
393
print(f" Traceback:")
394
traceback.print_exception(
395
type(error.original_error),
396
error.original_error,
397
error.original_error.__traceback__
398
)
399
400
# Logging middleware
401
class ErrorLoggingMiddleware:
402
def __init__(self, logger=None):
403
self.logger = logger or logging.getLogger(__name__)
404
405
def resolve(self, next_resolver, root, info, **args):
406
try:
407
result = next_resolver(root, info, **args)
408
return result
409
except GraphQLError as e:
410
# Log GraphQL errors with context
411
self.logger.error(
412
f"GraphQL error in {info.parent_type.name}.{info.field_name}: {e.message}",
413
extra={
414
'field_path': info.path,
415
'operation': info.operation.operation.value,
416
'variables': info.variable_values,
417
'extensions': e.extensions
418
}
419
)
420
raise
421
except Exception as e:
422
# Log and convert unexpected errors
423
self.logger.exception(
424
f"Unexpected error in {info.parent_type.name}.{info.field_name}",
425
extra={'field_path': info.path}
426
)
427
raise located_error(e, info.field_nodes, info.path)
428
```
429
430
## Types
431
432
```python { .api }
433
# Import required types
434
from typing import Any, Dict, List, Optional, Union, Sequence
435
from graphql.language import Node, Source, SourceLocation
436
from graphql.error import GraphQLError, GraphQLSyntaxError
437
438
# Error format types
439
GraphQLFormattedError = Dict[str, Any]
440
GraphQLErrorExtensions = Dict[str, Any]
441
442
# Core error classes
443
GraphQLError = class GraphQLError(Exception)
444
GraphQLSyntaxError = class GraphQLSyntaxError(GraphQLError)
445
446
# Error creation function
447
def located_error(
448
original_error: Exception,
449
nodes: Optional[Union[Node, Sequence[Node]]] = None,
450
path: Optional[Sequence[Union[str, int]]] = None,
451
) -> GraphQLError
452
453
# Location information
454
class SourceLocation:
455
line: int
456
column: int
457
458
# Execution result with errors
459
class ExecutionResult:
460
data: Optional[Dict[str, Any]]
461
errors: Optional[List[GraphQLError]]
462
extensions: Optional[Dict[str, Any]]
463
```