or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication.mdchange-feeds.mddatabase-management.mddocument-operations.mderror-handling.mdhttp-adapters.mdindex.mdquery-indexing.mdreplication.mdscheduler-monitoring.mdsecurity-document.mdviews-design-documents.md

error-handling.mddocs/

0

# Error Handling

1

2

Comprehensive exception hierarchy for handling different types of errors with specific exceptions for client, database, document, and operation-specific failures.

3

4

## Capabilities

5

6

### Exception Hierarchy

7

8

All Cloudant library exceptions inherit from the base `CloudantException` class.

9

10

```python { .api }

11

class CloudantException(Exception):

12

"""

13

Base exception class for all Cloudant library errors.

14

15

All other Cloudant exceptions inherit from this class, allowing

16

for comprehensive error handling with a single catch block.

17

"""

18

19

class CloudantArgumentError(CloudantException):

20

"""

21

Exception for invalid arguments passed to library functions.

22

23

Raised when:

24

- Required parameters are missing

25

- Parameter values are invalid or out of range

26

- Conflicting parameters are provided

27

"""

28

29

class ResultException(CloudantException):

30

"""

31

Exception for errors in result processing and iteration.

32

33

Raised when:

34

- Result iteration fails

35

- Result data is malformed

36

- Pagination errors occur

37

"""

38

39

class CloudantClientException(CloudantException):

40

"""

41

Exception for client connection and authentication errors.

42

43

Raised when:

44

- Authentication fails

45

- Connection to server fails

46

- Session management errors occur

47

- Network connectivity issues

48

"""

49

50

class CloudantDatabaseException(CloudantException):

51

"""

52

Exception for database-level operations.

53

54

Raised when:

55

- Database creation/deletion fails

56

- Database access denied

57

- Database metadata operations fail

58

- Query execution errors

59

"""

60

61

class CloudantDesignDocumentException(CloudantException):

62

"""

63

Exception for design document operations.

64

65

Raised when:

66

- Design document creation/update fails

67

- View definition errors

68

- Show/list function execution fails

69

- Update handler errors

70

"""

71

72

class CloudantDocumentException(CloudantException):

73

"""

74

Exception for document-level operations.

75

76

Raised when:

77

- Document CRUD operations fail

78

- Document conflict errors

79

- Attachment operations fail

80

- Document validation errors

81

"""

82

83

class CloudantFeedException(CloudantException):

84

"""

85

Exception for feed-related operations.

86

87

Raised when:

88

- Change feed connection fails

89

- Feed consumption errors

90

- Feed parameter validation fails

91

"""

92

93

class CloudantIndexException(CloudantException):

94

"""

95

Exception for index management operations.

96

97

Raised when:

98

- Index creation/deletion fails

99

- Index definition errors

100

- Index query optimization issues

101

"""

102

103

class CloudantReplicatorException(CloudantException):

104

"""

105

Exception for replication operations.

106

107

Raised when:

108

- Replication setup fails

109

- Replication monitoring errors

110

- Replication state management issues

111

"""

112

113

class CloudantViewException(CloudantException):

114

"""

115

Exception for view-related operations.

116

117

Raised when:

118

- View query execution fails

119

- View parameter validation errors

120

- MapReduce processing issues

121

"""

122

```

123

124

## Usage Examples

125

126

### Basic Error Handling

127

128

```python

129

from cloudant import cloudant

130

from cloudant.error import CloudantException

131

132

with cloudant('user', 'pass', account='myaccount') as client:

133

try:

134

# Any Cloudant operation

135

db = client['my_database']

136

doc = db['my_document']

137

doc.fetch()

138

139

except CloudantException as e:

140

print(f"Cloudant operation failed: {e}")

141

print(f"Error type: {type(e).__name__}")

142

```

143

144

### Specific Exception Handling

145

146

```python

147

from cloudant import cloudant

148

from cloudant.error import (

149

CloudantClientException,

150

CloudantDatabaseException,

151

CloudantDocumentException,

152

CloudantArgumentError

153

)

154

155

try:

156

with cloudant('invalid_user', 'invalid_pass', account='myaccount') as client:

157

# This will fail at connection time

158

db = client['my_database']

159

160

except CloudantClientException as e:

161

print(f"Authentication failed: {e}")

162

# Handle authentication error

163

# Could prompt for new credentials, use fallback auth, etc.

164

165

try:

166

with cloudant('user', 'pass', account='myaccount') as client:

167

# Try to access non-existent database

168

db = client['non_existent_database']

169

if not db.exists():

170

db.create()

171

172

except CloudantDatabaseException as e:

173

print(f"Database operation failed: {e}")

174

# Handle database error

175

# Could create database, use different database, etc.

176

177

try:

178

with cloudant('user', 'pass', account='myaccount') as client:

179

db = client['my_database']

180

doc = db['non_existent_document']

181

doc.fetch() # This will fail

182

183

except CloudantDocumentException as e:

184

print(f"Document operation failed: {e}")

185

# Handle document error

186

# Could create document, use default values, etc.

187

188

try:

189

with cloudant('user', 'pass', account='myaccount') as client:

190

# Invalid parameter

191

db = client.create_database('') # Empty name

192

193

except CloudantArgumentError as e:

194

print(f"Invalid argument: {e}")

195

# Handle argument error

196

# Could validate inputs, provide defaults, etc.

197

```

198

199

### Conflict Resolution

200

201

```python

202

from cloudant import cloudant

203

from cloudant.error import CloudantDocumentException

204

import time

205

import random

206

207

def save_with_retry(doc, max_retries=5):

208

"""Save document with automatic conflict resolution."""

209

210

for attempt in range(max_retries):

211

try:

212

doc.save()

213

print(f"Document saved successfully on attempt {attempt + 1}")

214

return True

215

216

except CloudantDocumentException as e:

217

error_msg = str(e).lower()

218

219

if 'conflict' in error_msg or '409' in str(e):

220

print(f"Conflict detected on attempt {attempt + 1}")

221

222

if attempt < max_retries - 1:

223

# Fetch latest version and retry

224

doc.fetch()

225

226

# Add random delay to reduce conflict probability

227

time.sleep(random.uniform(0.1, 0.5))

228

continue

229

else:

230

print(f"Max retries reached, conflict not resolved")

231

raise

232

else:

233

# Non-conflict error, don't retry

234

print(f"Non-conflict error: {e}")

235

raise

236

237

return False

238

239

# Usage

240

with cloudant('user', 'pass', account='myaccount') as client:

241

db = client['my_database']

242

doc = db['conflicted_document']

243

244

doc['updated_field'] = 'new_value'

245

doc['timestamp'] = time.time()

246

247

save_with_retry(doc)

248

```

249

250

### Network Error Handling

251

252

```python

253

from cloudant import cloudant

254

from cloudant.error import CloudantClientException

255

import time

256

257

def robust_operation(client, operation_func, max_retries=3):

258

"""Execute operation with retry on network errors."""

259

260

for attempt in range(max_retries):

261

try:

262

return operation_func(client)

263

264

except CloudantClientException as e:

265

error_msg = str(e).lower()

266

267

# Check if it's a network-related error

268

if any(keyword in error_msg for keyword in [

269

'timeout', 'connection', 'network', 'unreachable'

270

]):

271

print(f"Network error on attempt {attempt + 1}: {e}")

272

273

if attempt < max_retries - 1:

274

# Exponential backoff

275

wait_time = 2 ** attempt

276

print(f"Retrying in {wait_time} seconds...")

277

time.sleep(wait_time)

278

continue

279

else:

280

print("Max retries reached for network error")

281

raise

282

else:

283

# Non-network error, don't retry

284

print(f"Non-network client error: {e}")

285

raise

286

287

def my_database_operation(client):

288

"""Example database operation."""

289

db = client['my_database']

290

return db.all_docs(limit=10)

291

292

# Usage

293

with cloudant('user', 'pass', account='myaccount') as client:

294

try:

295

result = robust_operation(client, my_database_operation)

296

print(f"Operation successful: {len(list(result))} documents")

297

except CloudantClientException as e:

298

print(f"Operation failed permanently: {e}")

299

```

300

301

### Query Error Handling

302

303

```python

304

from cloudant import cloudant

305

from cloudant.error import CloudantDatabaseException, CloudantIndexException

306

307

def safe_query(db, selector, **kwargs):

308

"""Execute query with comprehensive error handling."""

309

310

try:

311

return db.get_query_result(selector, **kwargs)

312

313

except CloudantDatabaseException as e:

314

error_msg = str(e).lower()

315

316

if 'no_usable_index' in error_msg:

317

print("No suitable index found for query")

318

print("Suggestion: Create an index on the queried fields")

319

320

# Try to suggest index creation

321

fields = list(selector.keys())

322

print(f"Consider creating index on fields: {fields}")

323

324

# Could automatically create index here if desired

325

# db.create_query_index(fields=fields)

326

327

elif 'invalid_selector' in error_msg:

328

print(f"Invalid query selector: {selector}")

329

print("Check selector syntax and field names")

330

331

elif 'request_timeout' in error_msg:

332

print("Query timed out")

333

print("Try adding a limit or creating better indexes")

334

335

raise # Re-raise after logging

336

337

def safe_index_creation(db, fields, index_name):

338

"""Create index with error handling."""

339

340

try:

341

return db.create_query_index(

342

fields=fields,

343

index_name=index_name

344

)

345

346

except CloudantIndexException as e:

347

error_msg = str(e).lower()

348

349

if 'index_exists' in error_msg or 'conflict' in error_msg:

350

print(f"Index {index_name} already exists")

351

return None

352

353

elif 'invalid_fields' in error_msg:

354

print(f"Invalid index fields: {fields}")

355

print("Check field names and types")

356

357

raise

358

359

# Usage

360

with cloudant('user', 'pass', account='myaccount') as client:

361

db = client['my_database']

362

363

# Try query with error handling

364

try:

365

selector = {'type': 'user', 'status': 'active'}

366

result = safe_query(db, selector, limit=50)

367

368

for doc in result:

369

print(f"User: {doc.get('name', 'N/A')}")

370

371

except CloudantDatabaseException as e:

372

print(f"Query failed: {e}")

373

374

# Try index creation with error handling

375

try:

376

safe_index_creation(db, ['type', 'status'], 'type_status_idx')

377

print("Index created successfully")

378

except CloudantIndexException as e:

379

print(f"Index creation failed: {e}")

380

```

381

382

### Replication Error Handling

383

384

```python

385

from cloudant import cloudant

386

from cloudant.replicator import Replicator

387

from cloudant.error import CloudantReplicatorException

388

import time

389

390

def monitor_replication_with_error_handling(replicator, repl_id, timeout=300):

391

"""Monitor replication with comprehensive error handling."""

392

393

start_time = time.time()

394

395

while time.time() - start_time < timeout:

396

try:

397

state = replicator.replication_state(repl_id)

398

repl_state = state.get('_replication_state', 'unknown')

399

400

if repl_state == 'completed':

401

stats = state.get('_replication_stats', {})

402

docs_written = stats.get('docs_written', 0)

403

print(f"Replication completed: {docs_written} documents")

404

return True

405

406

elif repl_state == 'error':

407

error_reason = state.get('_replication_state_reason', 'Unknown error')

408

print(f"Replication failed: {error_reason}")

409

410

# Handle specific replication errors

411

if 'unauthorized' in error_reason.lower():

412

print("Authentication error - check credentials")

413

elif 'not_found' in error_reason.lower():

414

print("Database not found - check database names")

415

elif 'timeout' in error_reason.lower():

416

print("Network timeout - check connectivity")

417

418

return False

419

420

elif repl_state in ['triggered', 'running']:

421

stats = state.get('_replication_stats', {})

422

docs_read = stats.get('docs_read', 0)

423

docs_written = stats.get('docs_written', 0)

424

print(f"Replication progress: {docs_written}/{docs_read}")

425

426

time.sleep(5) # Check every 5 seconds

427

428

except CloudantReplicatorException as e:

429

print(f"Error checking replication state: {e}")

430

431

# If replication document was deleted, it might have completed

432

if 'not_found' in str(e).lower():

433

print("Replication document not found - may have completed")

434

return None

435

436

time.sleep(10) # Wait longer on error

437

438

print(f"Replication monitoring timed out after {timeout} seconds")

439

return None

440

441

# Usage

442

with cloudant('user', 'pass', account='myaccount') as client:

443

replicator = Replicator(client)

444

445

try:

446

# Start replication with error handling

447

repl_doc = replicator.create_replication(

448

source_db='source_db',

449

target_db='target_db',

450

create_target=True

451

)

452

453

repl_id = repl_doc['_id']

454

print(f"Started replication: {repl_id}")

455

456

# Monitor with error handling

457

success = monitor_replication_with_error_handling(replicator, repl_id)

458

459

if success:

460

print("Replication completed successfully")

461

elif success is False:

462

print("Replication failed")

463

else:

464

print("Replication status uncertain")

465

466

except CloudantReplicatorException as e:

467

print(f"Failed to start replication: {e}")

468

```

469

470

### Feed Error Handling

471

472

```python

473

from cloudant import cloudant

474

from cloudant.error import CloudantFeedException

475

import time

476

477

def robust_change_feed(db, max_reconnects=5):

478

"""Change feed with automatic reconnection on errors."""

479

480

reconnect_count = 0

481

last_seq = '0'

482

483

while reconnect_count < max_reconnects:

484

try:

485

print(f"Starting change feed from sequence: {last_seq}")

486

487

changes = db.changes(

488

since=last_seq,

489

feed='continuous',

490

include_docs=True,

491

heartbeat=30000,

492

timeout=60000

493

)

494

495

for change in changes:

496

if change: # Skip heartbeat messages

497

doc_id = change['id']

498

last_seq = change['seq']

499

500

print(f"Change detected: {doc_id} (seq: {last_seq})")

501

502

# Process change here

503

if change.get('deleted'):

504

print(f"Document deleted: {doc_id}")

505

else:

506

doc = change.get('doc', {})

507

print(f"Document updated: {doc_id}")

508

509

# If we reach here, feed ended normally

510

print("Change feed ended normally")

511

break

512

513

except CloudantFeedException as e:

514

reconnect_count += 1

515

error_msg = str(e).lower()

516

517

print(f"Feed error (attempt {reconnect_count}/{max_reconnects}): {e}")

518

519

if 'timeout' in error_msg or 'connection' in error_msg:

520

print("Network-related error, will retry")

521

elif 'unauthorized' in error_msg:

522

print("Authentication error, check credentials")

523

break

524

elif 'not_found' in error_msg:

525

print("Database not found")

526

break

527

528

if reconnect_count < max_reconnects:

529

wait_time = min(2 ** reconnect_count, 60) # Cap at 60 seconds

530

print(f"Reconnecting in {wait_time} seconds...")

531

time.sleep(wait_time)

532

else:

533

print("Max reconnection attempts reached")

534

raise

535

536

except KeyboardInterrupt:

537

print("Change feed interrupted by user")

538

break

539

except Exception as e:

540

print(f"Unexpected error in change feed: {e}")

541

break

542

543

# Usage

544

with cloudant('user', 'pass', account='myaccount') as client:

545

db = client['my_database']

546

547

try:

548

robust_change_feed(db)

549

except CloudantFeedException as e:

550

print(f"Change feed failed permanently: {e}")

551

```

552

553

### Error Context and Debugging

554

555

```python

556

from cloudant import cloudant

557

from cloudant.error import CloudantException

558

import traceback

559

import logging

560

561

# Set up logging for better error tracking

562

logging.basicConfig(

563

level=logging.INFO,

564

format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'

565

)

566

logger = logging.getLogger('cloudant_app')

567

568

def detailed_error_handling():

569

"""Example of detailed error context extraction."""

570

571

try:

572

with cloudant('user', 'pass', account='myaccount') as client:

573

db = client['my_database']

574

575

# Some operation that might fail

576

doc = db['problem_document']

577

doc.fetch()

578

579

except CloudantException as e:

580

# Log detailed error information

581

logger.error(f"Cloudant operation failed:")

582

logger.error(f" Error type: {type(e).__name__}")

583

logger.error(f" Error message: {str(e)}")

584

585

# Check if there's additional error context

586

if hasattr(e, 'response'):

587

logger.error(f" HTTP status: {e.response.status_code}")

588

logger.error(f" Response headers: {dict(e.response.headers)}")

589

590

if hasattr(e, 'request'):

591

logger.error(f" Request URL: {e.request.url}")

592

logger.error(f" Request method: {e.request.method}")

593

594

# Full stack trace for debugging

595

logger.debug("Full stack trace:")

596

logger.debug(traceback.format_exc())

597

598

# Re-raise with additional context

599

raise type(e)(f"Operation failed with context: {str(e)}") from e

600

601

def error_recovery_example():

602

"""Example of implementing error recovery strategies."""

603

604

max_retries = 3

605

retry_count = 0

606

607

while retry_count < max_retries:

608

try:

609

with cloudant('user', 'pass', account='myaccount') as client:

610

db = client['my_database']

611

612

# Critical operation

613

doc = db.create_document({

614

'type': 'important_record',

615

'data': 'critical_data',

616

'timestamp': time.time()

617

})

618

619

logger.info(f"Document created successfully: {doc['_id']}")

620

return doc

621

622

except CloudantDocumentException as e:

623

retry_count += 1

624

logger.warning(f"Document operation failed (attempt {retry_count}): {e}")

625

626

if retry_count < max_retries:

627

# Implement recovery strategy

628

if 'conflict' in str(e).lower():

629

# Generate new ID for conflicts

630

logger.info("Generating new document ID due to conflict")

631

time.sleep(0.5) # Brief delay

632

else:

633

# General retry with exponential backoff

634

wait_time = 2 ** retry_count

635

logger.info(f"Retrying in {wait_time} seconds...")

636

time.sleep(wait_time)

637

else:

638

logger.error("Max retries exceeded, operation failed permanently")

639

raise

640

641

except CloudantClientException as e:

642

logger.error(f"Client error, cannot retry: {e}")

643

raise

644

except Exception as e:

645

logger.error(f"Unexpected error: {e}")

646

raise

647

648

return None

649

650

# Usage examples

651

if __name__ == "__main__":

652

try:

653

detailed_error_handling()

654

except CloudantException as e:

655

print(f"Final error: {e}")

656

657

try:

658

doc = error_recovery_example()

659

if doc:

660

print(f"Operation succeeded: {doc['_id']}")

661

except CloudantException as e:

662

print(f"Operation failed permanently: {e}")

663

```

664

665

## Best Practices

666

667

### Error Handling Guidelines

668

669

1. **Use Specific Exceptions**: Catch specific exception types rather than the generic `CloudantException` when you need different handling logic.

670

671

2. **Implement Retry Logic**: For network-related errors, implement exponential backoff retry strategies.

672

673

3. **Log Error Context**: Include error type, message, and relevant context (HTTP status, request details) in logs.

674

675

4. **Graceful Degradation**: Design your application to continue functioning with reduced capabilities when possible.

676

677

5. **Monitor Error Patterns**: Track error frequencies and types to identify system issues early.

678

679

6. **Validate Inputs**: Use `CloudantArgumentError` to validate parameters before making requests.

680

681

7. **Handle Conflicts**: Implement conflict resolution strategies for document operations in multi-user environments.

682

683

8. **Set Appropriate Timeouts**: Use reasonable timeout values to prevent indefinite blocking while allowing sufficient time for operations.

684

685

9. **Resource Cleanup**: Always ensure proper cleanup of resources (connections, feeds) in error scenarios.

686

687

10. **User-Friendly Messages**: Convert technical error messages into user-friendly explanations when appropriate.