or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-schema.mderror-handling.mdindex.mdrange-collection-validators.mdstring-pattern-validators.mdtype-validators.mdutility-transformers.mdvalidation-composers.md

error-handling.mddocs/

0

# Error Handling

1

2

Comprehensive exception hierarchy for validation errors with path tracking, multiple error aggregation, and detailed error reporting. Voluptuous provides structured error handling for complex validation scenarios.

3

4

## Capabilities

5

6

### Base Exception Classes

7

8

Foundation exception classes for all validation errors.

9

10

```python { .api }

11

class Error(Exception):

12

"""

13

Base validation exception class.

14

15

All voluptuous exceptions inherit from this class, making it easy

16

to catch any validation-related error.

17

"""

18

19

class SchemaError(Error):

20

"""

21

Schema definition errors.

22

23

Raised when there are problems with the schema definition itself,

24

not with the data being validated.

25

26

Examples:

27

- Invalid schema structure

28

- Conflicting marker requirements

29

- Malformed validation rules

30

"""

31

```

32

33

### Primary Validation Exceptions

34

35

Core exception classes for validation failures.

36

37

```python { .api }

38

class Invalid(Error):

39

def __init__(self, message, path=None, error_message=None, error_type=None):

40

"""

41

Base class for validation errors.

42

43

Parameters:

44

- message: Error message to display

45

- path: List of keys representing path to error location

46

- error_message: Original error message (before path formatting)

47

- error_type: Type of error for categorization

48

49

Properties:

50

- msg: The error message

51

- path: Path to error location as list of keys

52

- error_message: Original error message

53

"""

54

55

@property

56

def msg(self):

57

"""Get the error message."""

58

59

@property

60

def path(self):

61

"""Get the path to error location as list of keys."""

62

63

@property

64

def error_message(self):

65

"""Get the original error message."""

66

67

def prepend(self, path):

68

"""

69

Add path prefix to error location.

70

71

Parameters:

72

- path: List of keys to prepend to current path

73

"""

74

75

class MultipleInvalid(Invalid):

76

def __init__(self, errors=None):

77

"""

78

Container for multiple validation errors.

79

80

Parameters:

81

- errors: List of Invalid exceptions

82

83

This exception aggregates multiple validation errors

84

and provides access to individual errors while presenting

85

as a single exception.

86

"""

87

88

def add(self, error):

89

"""

90

Add another error to the collection.

91

92

Parameters:

93

- error: Invalid exception to add

94

"""

95

```

96

97

**Usage Examples:**

98

99

```python

100

from voluptuous import Schema, Invalid, MultipleInvalid, Required, All, Length, Range

101

102

# Basic error handling

103

user_schema = Schema({

104

Required('name'): All(str, Length(min=1)),

105

Required('age'): All(int, Range(min=0, max=150)),

106

})

107

108

try:

109

result = user_schema({'name': '', 'age': -5})

110

except MultipleInvalid as e:

111

print("Multiple validation errors:")

112

for error in e.errors:

113

print(f" {error.path}: {error.msg}")

114

except Invalid as e:

115

print(f"Validation error at {e.path}: {e.msg}")

116

117

# Error path tracking

118

nested_schema = Schema({

119

'users': [{

120

Required('name'): str,

121

Required('email'): Email(),

122

}]

123

})

124

125

try:

126

nested_schema({

127

'users': [

128

{'name': 'John', 'email': 'invalid-email'},

129

{'name': '', 'email': 'jane@example.com'},

130

]

131

})

132

except MultipleInvalid as e:

133

for error in e.errors:

134

print(f"Error at {error.path}: {error.msg}")

135

# Output might be:

136

# Error at ['users', 0, 'email']: not a valid email address

137

# Error at ['users', 1, 'name']: length of value must be at least 1

138

```

139

140

### Specific Exception Types

141

142

Detailed exception classes for specific validation failure types.

143

144

**Schema Structure Errors:**

145

146

```python { .api }

147

class RequiredFieldInvalid(Invalid):

148

"""Required field was missing from input data."""

149

150

class ObjectInvalid(Invalid):

151

"""Expected an object but received a different type."""

152

153

class DictInvalid(Invalid):

154

"""Expected a dictionary but received a different type."""

155

156

class ExclusiveInvalid(Invalid):

157

"""Multiple values found in exclusion group (only one allowed)."""

158

159

class InclusiveInvalid(Invalid):

160

"""Not all values found in inclusion group (all or none required)."""

161

162

class SequenceTypeInvalid(Invalid):

163

"""Expected a sequence type but received something else."""

164

```

165

166

**Type and Value Errors:**

167

168

```python { .api }

169

class TypeInvalid(Invalid):

170

"""Value was not of the required type."""

171

172

class ValueInvalid(Invalid):

173

"""Value was found invalid by evaluation function."""

174

175

class ScalarInvalid(Invalid):

176

"""Scalar values did not match expected values."""

177

178

class CoerceInvalid(Invalid):

179

"""Impossible to coerce value to target type."""

180

181

class LiteralInvalid(Invalid):

182

"""Literal value did not match expected literal."""

183

```

184

185

**Validation Logic Errors:**

186

187

```python { .api }

188

class AnyInvalid(Invalid):

189

"""Value did not pass any validator in Any() composition."""

190

191

class AllInvalid(Invalid):

192

"""Value did not pass all validators in All() composition."""

193

194

class NotEnoughValid(Invalid):

195

"""Value did not pass enough validations in SomeOf()."""

196

197

class TooManyValid(Invalid):

198

"""Value passed more than expected validations in SomeOf()."""

199

```

200

201

**Pattern and Format Errors:**

202

203

```python { .api }

204

class MatchInvalid(Invalid):

205

"""Value does not match the given regular expression."""

206

207

class RangeInvalid(Invalid):

208

"""Value is not in the specified range."""

209

210

class LengthInvalid(Invalid):

211

"""Length of value is not within specified bounds."""

212

213

class DatetimeInvalid(Invalid):

214

"""Value is not a valid datetime string."""

215

216

class DateInvalid(Invalid):

217

"""Value is not a valid date string."""

218

```

219

220

**Boolean Validation Errors:**

221

222

```python { .api }

223

class TrueInvalid(Invalid):

224

"""Value is not True when True was required."""

225

226

class FalseInvalid(Invalid):

227

"""Value is not False when False was required."""

228

229

class BooleanInvalid(Invalid):

230

"""Value is not a valid boolean representation."""

231

```

232

233

**Network and File System Errors:**

234

235

```python { .api }

236

class UrlInvalid(Invalid):

237

"""Value is not a valid URL format."""

238

239

class EmailInvalid(Invalid):

240

"""Value is not a valid email address."""

241

242

class FileInvalid(Invalid):

243

"""Value is not a valid file path or file does not exist."""

244

245

class DirInvalid(Invalid):

246

"""Value is not a valid directory path or directory does not exist."""

247

248

class PathInvalid(Invalid):

249

"""Value is not a valid path or path does not exist."""

250

```

251

252

**Collection Errors:**

253

254

```python { .api }

255

class ContainsInvalid(Invalid):

256

"""Required item not found in sequence."""

257

258

class InInvalid(Invalid):

259

"""Value not found in allowed container."""

260

261

class NotInInvalid(Invalid):

262

"""Value found in forbidden container."""

263

264

class ExactSequenceInvalid(Invalid):

265

"""Sequence does not match expected exact structure."""

266

```

267

268

### Error Handling Patterns

269

270

Common patterns for handling validation errors effectively.

271

272

**Granular Error Handling:**

273

274

```python

275

from voluptuous import Schema, Invalid, MultipleInvalid, Required, Email, Range

276

277

def validate_user_data(data):

278

"""Validate user data with specific error handling."""

279

schema = Schema({

280

Required('name'): str,

281

Required('email'): Email(),

282

Required('age'): Range(min=18, max=120),

283

})

284

285

try:

286

return schema(data)

287

except EmailInvalid:

288

raise ValueError("Please provide a valid email address")

289

except RangeInvalid as e:

290

if 'age' in str(e.path):

291

raise ValueError("Age must be between 18 and 120")

292

raise

293

except RequiredFieldInvalid as e:

294

field_name = e.path[-1] if e.path else 'unknown field'

295

raise ValueError(f"Required field '{field_name}' is missing")

296

except Invalid as e:

297

raise ValueError(f"Validation error: {e}")

298

```

299

300

**Error Collection and Reporting:**

301

302

```python

303

from voluptuous import Schema, MultipleInvalid, Invalid

304

305

def collect_all_errors(schema, data):

306

"""Collect all validation errors for comprehensive reporting."""

307

try:

308

return schema(data), []

309

except MultipleInvalid as e:

310

errors = []

311

for error in e.errors:

312

errors.append({

313

'path': error.path,

314

'message': error.msg,

315

'type': type(error).__name__,

316

})

317

return None, errors

318

except Invalid as e:

319

return None, [{

320

'path': e.path,

321

'message': e.msg,

322

'type': type(e).__name__,

323

}]

324

325

# Usage

326

schema = Schema({

327

Required('users'): [{

328

Required('name'): All(str, Length(min=1)),

329

Required('email'): Email(),

330

}]

331

})

332

333

data = {

334

'users': [

335

{'name': '', 'email': 'invalid'},

336

{'name': 'John'}, # Missing email

337

]

338

}

339

340

result, errors = collect_all_errors(schema, data)

341

if errors:

342

for error in errors:

343

print(f"Error at {'.'.join(map(str, error['path']))}: {error['message']}")

344

```

345

346

**Custom Error Messages:**

347

348

```python

349

from voluptuous import Schema, message, All, Length, Range, Email

350

351

# Custom error messages with decorators

352

@message("Username must be 3-20 characters long")

353

def username_length(value):

354

return Length(min=3, max=20)(value)

355

356

@message("Age must be between 13 and 120", ValueError)

357

def valid_age(value):

358

return Range(min=13, max=120)(value)

359

360

# Schema with custom messages

361

user_schema = Schema({

362

'username': All(str, username_length),

363

'age': All(int, valid_age),

364

'email': message(Email(), "Please enter a valid email address"),

365

})

366

367

try:

368

user_schema({'username': 'ab', 'age': 5, 'email': 'invalid'})

369

except MultipleInvalid as e:

370

for error in e.errors:

371

print(error.msg) # Uses custom messages

372

```

373

374

**Error Recovery and Partial Validation:**

375

376

```python

377

from voluptuous import Schema, Invalid, Required, Optional

378

379

def partial_validate(schema, data):

380

"""Validate data and return partial results with errors."""

381

validated = {}

382

errors = {}

383

384

if isinstance(schema.schema, dict):

385

for key, validator in schema.schema.items():

386

field_name = key.schema if hasattr(key, 'schema') else key

387

388

try:

389

if field_name in data:

390

validated[field_name] = validator(data[field_name])

391

elif isinstance(key, Required):

392

if hasattr(key, 'default') and key.default is not UNDEFINED:

393

validated[field_name] = key.default() if callable(key.default) else key.default

394

else:

395

errors[field_name] = "Required field missing"

396

except Invalid as e:

397

errors[field_name] = str(e)

398

399

return validated, errors

400

401

# Usage for form validation with partial success

402

form_schema = Schema({

403

Required('name'): All(str, Length(min=1)),

404

Required('email'): Email(),

405

Optional('age'): All(int, Range(min=0, max=150)),

406

})

407

408

form_data = {

409

'name': 'John Doe', # Valid

410

'email': 'invalid-email', # Invalid

411

'age': 25, # Valid

412

}

413

414

validated, errors = partial_validate(form_schema, form_data)

415

print("Validated fields:", validated) # {'name': 'John Doe', 'age': 25}

416

print("Error fields:", errors) # {'email': 'not a valid email address'}

417

```

418

419

**Contextual Error Enhancement:**

420

421

```python

422

from voluptuous import Schema, Invalid, MultipleInvalid

423

424

def enhance_errors(errors, context=None):

425

"""Add contextual information to validation errors."""

426

enhanced = []

427

428

for error in (errors.errors if isinstance(errors, MultipleInvalid) else [errors]):

429

enhanced_msg = error.msg

430

431

# Add field-specific context

432

if error.path:

433

field_name = error.path[-1]

434

if field_name == 'email':

435

enhanced_msg += " (example: user@example.com)"

436

elif field_name == 'phone':

437

enhanced_msg += " (example: +1-555-123-4567)"

438

elif field_name == 'age':

439

enhanced_msg += " (must be a number between 0 and 150)"

440

441

# Add context information

442

if context:

443

enhanced_msg += f" [Context: {context}]"

444

445

enhanced.append({

446

'path': error.path,

447

'message': enhanced_msg,

448

'original': error.msg,

449

'type': type(error).__name__,

450

})

451

452

return enhanced

453

454

# Usage

455

try:

456

schema({'email': 'invalid', 'age': 'not-a-number'})

457

except (Invalid, MultipleInvalid) as e:

458

enhanced = enhance_errors(e, context="User registration form")

459

for error in enhanced:

460

print(f"{error['path']}: {error['message']}")

461

```

462

463

### Humanized Error Messages

464

465

Voluptuous includes utilities for creating more user-friendly error messages in the `voluptuous.humanize` module.

466

467

```python { .api }

468

from voluptuous.humanize import humanize_error, validate_with_humanized_errors

469

470

def humanize_error(data, validation_error, max_sub_error_length=500):

471

"""

472

Provide more helpful validation error messages.

473

474

Parameters:

475

- data: Original data that failed validation

476

- validation_error: Invalid or MultipleInvalid exception

477

- max_sub_error_length: Maximum length for displaying offending values

478

479

Returns:

480

Human-readable error message string including offending values

481

"""

482

483

def validate_with_humanized_errors(data, schema, max_sub_error_length=500):

484

"""

485

Validate data and raise Error with humanized message on failure.

486

487

Parameters:

488

- data: Data to validate

489

- schema: Schema to validate against

490

- max_sub_error_length: Maximum length for error values

491

492

Returns:

493

Validated data if successful

494

495

Raises:

496

Error: With humanized error message if validation fails

497

"""

498

```

499

500

**Usage Examples:**

501

502

```python

503

from voluptuous import Schema, Required, Email, Range, All, Length, Error

504

from voluptuous.humanize import humanize_error, validate_with_humanized_errors

505

506

schema = Schema({

507

Required('users'): [{

508

Required('name'): All(str, Length(min=1)),

509

Required('email'): Email(),

510

Required('age'): Range(min=0, max=150),

511

}]

512

})

513

514

data = {

515

'users': [

516

{'name': '', 'email': 'not-an-email', 'age': 200}

517

]

518

}

519

520

try:

521

result = validate_with_humanized_errors(data, schema)

522

except Error as e:

523

print(e)

524

# Output includes the actual offending values:

525

# "not a valid email address @ data['users'][0]['email']. Got 'not-an-email'"

526

# "value must be at most 150 @ data['users'][0]['age']. Got 200"

527

528

# Or manually humanize errors

529

try:

530

schema(data)

531

except (Invalid, MultipleInvalid) as e:

532

friendly_message = humanize_error(data, e)

533

print(friendly_message)

534

```