or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

error-handling.mdexecution-engine.mdexecution.mdindex.mdlanguage.mdtype-system.mdutilities.mdvalidation.md

error-handling.mddocs/

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

```