or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

code-generation.mdcore-api.mderror-handling.mdindex.mdpreconf-converters.mdstrategies.md

error-handling.mddocs/

0

# Error Handling and Validation

1

2

Comprehensive error handling system with detailed validation errors, structured exception hierarchies, and error transformation utilities. Cattrs provides rich error information to help debug data structuring issues and provide meaningful feedback to users.

3

4

## Capabilities

5

6

### Exception Hierarchy

7

8

Structured exception classes that inherit from Python's ExceptionGroup for detailed error reporting.

9

10

```python { .api }

11

from cattrs.errors import (

12

BaseValidationError,

13

ClassValidationError,

14

IterableValidationError,

15

AttributeValidationNote,

16

IterableValidationNote,

17

ForbiddenExtraKeysError,

18

StructureHandlerNotFoundError

19

)

20

21

class BaseValidationError(ExceptionGroup):

22

"""

23

Base class for validation errors (ExceptionGroup subclass).

24

25

Provides structured error information with nested exception details

26

for complex data validation scenarios.

27

"""

28

def __init__(self, message, exceptions, cl=None):

29

"""

30

Initialize validation error.

31

32

Parameters:

33

- message: Human-readable error message

34

- exceptions: List of nested exceptions

35

- cl: The class being validated (optional)

36

"""

37

38

class ClassValidationError(BaseValidationError):

39

"""

40

Raised when validating a class with invalid attributes.

41

42

Contains detailed information about which attributes failed validation

43

and why, including nested errors for complex data structures.

44

"""

45

def __init__(self, message, exceptions, cl):

46

"""

47

Initialize class validation error.

48

49

Parameters:

50

- message: Error message describing the validation failure

51

- exceptions: List of attribute-specific exceptions

52

- cl: The class that failed validation

53

"""

54

55

class IterableValidationError(BaseValidationError):

56

"""

57

Raised when structuring an iterable fails.

58

59

Provides information about which elements in the iterable failed

60

to structure and the specific errors for each element.

61

"""

62

def __init__(self, message, exceptions, cl):

63

"""Initialize iterable validation error."""

64

65

class AttributeValidationNote:

66

"""

67

Note attached to exceptions when attribute structuring fails.

68

69

Provides context about which attribute caused the error and

70

additional metadata for debugging.

71

"""

72

def __init__(self, name, cl, converter):

73

"""

74

Initialize attribute validation note.

75

76

Parameters:

77

- name: Name of the attribute that failed

78

- cl: Class containing the attribute

79

- converter: Converter instance used

80

"""

81

self.name = name

82

self.cl = cl

83

self.converter = converter

84

85

class IterableValidationNote:

86

"""

87

Note attached to exceptions when iterable element structuring fails.

88

89

Provides context about which element index caused the error.

90

"""

91

def __init__(self, index, cl, converter):

92

"""

93

Initialize iterable validation note.

94

95

Parameters:

96

- index: Index of the element that failed

97

- cl: Target type for the iterable elements

98

- converter: Converter instance used

99

"""

100

self.index = index

101

self.cl = cl

102

self.converter = converter

103

104

class ForbiddenExtraKeysError(Exception):

105

"""

106

Raised when extra keys are found and forbidden.

107

108

Occurs when a converter is configured with forbid_extra_keys=True

109

and the input data contains keys not present in the target class.

110

"""

111

def __init__(self, message, cl, extra_keys):

112

"""

113

Initialize forbidden extra keys error.

114

115

Parameters:

116

- message: Error message

117

- cl: Target class

118

- extra_keys: Set of forbidden keys found

119

"""

120

self.cl = cl

121

self.extra_keys = extra_keys

122

super().__init__(message)

123

124

class StructureHandlerNotFoundError(Exception):

125

"""

126

Raised when no structure handler found for a type.

127

128

Occurs when the converter doesn't know how to structure a particular

129

type and no appropriate hook has been registered.

130

"""

131

def __init__(self, message, type_):

132

"""

133

Initialize structure handler not found error.

134

135

Parameters:

136

- message: Error message

137

- type_: The type that couldn't be handled

138

"""

139

self.type = type_

140

super().__init__(message)

141

```

142

143

### Error Transformation

144

145

Utilities for transforming validation errors into user-friendly formats.

146

147

```python { .api }

148

from cattrs.v import transform_error, format_exception

149

150

def transform_error(

151

exc,

152

path=None,

153

**kwargs

154

):

155

"""

156

Transform validation errors into detailed error message lists.

157

158

Converts complex nested validation errors into a flat list of

159

human-readable error messages with path information.

160

161

Parameters:

162

- exc: The exception to transform

163

- path: Current path context for nested errors

164

- **kwargs: Additional transformation options

165

166

Returns:

167

List of transformed error messages with path information

168

"""

169

170

def format_exception(exc, path=None):

171

"""

172

Format exceptions into readable error messages.

173

174

Provides a human-readable representation of validation errors

175

with context about where in the data structure the error occurred.

176

177

Parameters:

178

- exc: Exception to format

179

- path: Path context for the error

180

181

Returns:

182

Formatted error message string

183

"""

184

```

185

186

## Usage Examples

187

188

### Basic Error Handling

189

190

```python

191

from cattrs import structure, ClassValidationError

192

from attrs import define

193

194

@define

195

class User:

196

name: str

197

age: int

198

email: str

199

200

try:

201

# Invalid data - age should be int, not string

202

invalid_data = {"name": "Alice", "age": "not-a-number", "email": "alice@example.com"}

203

user = structure(invalid_data, User)

204

except ClassValidationError as e:

205

print(f"Validation failed for {e.cl.__name__}")

206

for exc in e.exceptions:

207

print(f" - {exc}")

208

# Output shows detailed information about the age field validation failure

209

```

210

211

### Nested Structure Error Handling

212

213

```python

214

from cattrs import structure, ClassValidationError

215

from attrs import define

216

from typing import List

217

218

@define

219

class Address:

220

street: str

221

zip_code: int

222

223

@define

224

class Person:

225

name: str

226

addresses: List[Address]

227

228

try:

229

# Invalid nested data

230

invalid_data = {

231

"name": "Bob",

232

"addresses": [

233

{"street": "123 Main St", "zip_code": 12345},

234

{"street": "456 Oak Ave", "zip_code": "invalid"} # Invalid zip_code

235

]

236

}

237

person = structure(invalid_data, Person)

238

except ClassValidationError as e:

239

print(f"Person validation failed:")

240

for exc in e.exceptions:

241

if hasattr(exc, 'exceptions'): # Nested validation error

242

print(f" Address list error:")

243

for nested_exc in exc.exceptions:

244

print(f" - {nested_exc}")

245

else:

246

print(f" - {exc}")

247

```

248

249

### Error Transformation for User Feedback

250

251

```python

252

from cattrs import structure, ClassValidationError

253

from cattrs.v import transform_error

254

255

@define

256

class Config:

257

host: str

258

port: int

259

ssl_enabled: bool

260

261

def validate_config(config_data):

262

try:

263

return structure(config_data, Config)

264

except ClassValidationError as e:

265

# Transform errors into user-friendly messages

266

error_messages = transform_error(e)

267

268

print("Configuration validation failed:")

269

for error_msg in error_messages:

270

print(f" - {error_msg}")

271

272

return None

273

274

# Usage

275

invalid_config = {

276

"host": "localhost",

277

"port": "not-a-number", # Invalid

278

"ssl_enabled": "yes" # Invalid - should be boolean

279

}

280

281

config = validate_config(invalid_config)

282

# Outputs detailed, user-friendly error messages

283

```

284

285

### Handling Extra Keys

286

287

```python

288

from cattrs import Converter, ForbiddenExtraKeysError

289

from attrs import define

290

291

@define

292

class Settings:

293

debug: bool

294

log_level: str

295

296

# Configure converter to forbid extra keys

297

converter = Converter(forbid_extra_keys=True)

298

299

try:

300

# Data with extra keys

301

data_with_extra = {

302

"debug": True,

303

"log_level": "INFO",

304

"unknown_setting": "value" # This will cause an error

305

}

306

settings = converter.structure(data_with_extra, Settings)

307

except ForbiddenExtraKeysError as e:

308

print(f"Extra keys not allowed in {e.cl.__name__}: {e.extra_keys}")

309

# Output: Extra keys not allowed in Settings: {'unknown_setting'}

310

```

311

312

### Custom Error Handling with Hooks

313

314

```python

315

from cattrs import Converter, register_structure_hook

316

from datetime import datetime

317

318

def safe_datetime_structure(value, _):

319

if isinstance(value, str):

320

try:

321

return datetime.fromisoformat(value)

322

except ValueError as e:

323

raise ValueError(f"Invalid datetime format: {value}. Expected ISO format.") from e

324

elif isinstance(value, (int, float)):

325

try:

326

return datetime.fromtimestamp(value)

327

except (ValueError, OSError) as e:

328

raise ValueError(f"Invalid timestamp: {value}") from e

329

else:

330

raise TypeError(f"Cannot convert {type(value)} to datetime")

331

332

converter = Converter()

333

converter.register_structure_hook(datetime, safe_datetime_structure)

334

335

@define

336

class Event:

337

name: str

338

timestamp: datetime

339

340

try:

341

# This will provide a clear error message

342

invalid_event = {"name": "Meeting", "timestamp": "not-a-datetime"}

343

event = converter.structure(invalid_event, Event)

344

except ClassValidationError as e:

345

print("Event validation failed:")

346

for exc in e.exceptions:

347

print(f" - {exc}")

348

# Output includes custom error message about datetime format

349

```

350

351

### Comprehensive Error Reporting

352

353

```python

354

from cattrs import structure, ClassValidationError

355

from cattrs.v import transform_error

356

from attrs import define

357

from typing import List, Optional

358

359

@define

360

class Contact:

361

email: str

362

phone: Optional[str] = None

363

364

@define

365

class Company:

366

name: str

367

employees: List[Contact]

368

founded_year: int

369

370

def validate_company_data(data):

371

try:

372

return structure(data, Company)

373

except ClassValidationError as e:

374

print(f"\n=== Validation Report for {e.cl.__name__} ===")

375

376

# Use transform_error for detailed path information

377

errors = transform_error(e)

378

379

for i, error in enumerate(errors, 1):

380

print(f"{i}. {error}")

381

382

print(f"\nTotal errors: {len(errors)}")

383

return None

384

385

# Test with complex invalid data

386

complex_invalid_data = {

387

"name": "", # Invalid - empty string

388

"employees": [

389

{"email": "valid@example.com"}, # Valid

390

{"email": "invalid-email", "phone": "123-456-7890"}, # Invalid email

391

{"email": "", "phone": None} # Invalid - empty email

392

],

393

"founded_year": "not-a-year" # Invalid - should be int

394

}

395

396

company = validate_company_data(complex_invalid_data)

397

# Provides comprehensive error report with paths and specific issues

398

```

399

400

## Integration with Detailed Validation

401

402

Cattrs converters can be configured for detailed validation to provide enhanced error information:

403

404

```python

405

from cattrs import Converter

406

407

# Enable detailed validation (default in newer versions)

408

converter = Converter(detailed_validation=True)

409

410

# This provides more context in error messages and better path tracking

411

# for nested data structures

412

```

413

414

## Types

415

416

```python { .api }

417

from typing import List, Any, Type, Set, Optional

418

419

# Error transformation result type

420

ErrorMessage = str

421

ErrorPath = str

422

TransformedErrors = List[ErrorMessage]

423

424

# Exception context types

425

ExceptionContext = Any

426

ValidationPath = List[str]

427

```