0
# Exception Classes and Error Handling
1
2
Comprehensive exception hierarchy mapping HTTP status codes and Salesforce errors to specific Python exceptions for precise error handling and debugging. The simple-salesforce library provides detailed error information to help developers identify and resolve issues quickly.
3
4
## Base Exception Class
5
6
The foundation exception class that all other Salesforce-specific exceptions inherit from.
7
8
```python { .api }
9
class SalesforceError(Exception):
10
def __init__(self, url, status, resource_name, content):
11
"""
12
Base exception for all Salesforce API errors.
13
14
Parameters:
15
- url: API endpoint URL where error occurred
16
- status: HTTP status code
17
- resource_name: Salesforce resource or operation name
18
- content: Response content with error details
19
20
Attributes:
21
- url: The API endpoint URL
22
- status: HTTP status code
23
- resource_name: Resource identifier
24
- content: Raw error response content
25
"""
26
self.url = url
27
self.status = status
28
self.resource_name = resource_name
29
self.content = content
30
31
# Create detailed error message
32
message = f"Error Code: {status}"
33
if resource_name:
34
message += f", Resource: {resource_name}"
35
if url:
36
message += f", URL: {url}"
37
38
super().__init__(message)
39
```
40
41
## HTTP Status Code Exceptions
42
43
Specific exceptions mapped to HTTP status codes for precise error handling.
44
45
```python { .api }
46
class SalesforceMoreThanOneRecord(SalesforceError):
47
"""
48
Error Code 300: Multiple Choice
49
50
Raised when a query or operation returns multiple records but only one was expected.
51
Common in get operations with non-unique identifiers.
52
"""
53
54
class SalesforceMalformedRequest(SalesforceError):
55
"""
56
Error Code 400: Bad Request
57
58
Raised when the request is malformed, has invalid parameters, or contains
59
invalid data. Check request syntax, required fields, and data formats.
60
"""
61
62
class SalesforceExpiredSession(SalesforceError):
63
"""
64
Error Code 401: Unauthorized
65
66
Raised when the session has expired or authentication credentials are invalid.
67
Requires re-authentication or session refresh.
68
"""
69
70
class SalesforceRefusedRequest(SalesforceError):
71
"""
72
Error Code 403: Forbidden
73
74
Raised when the request is refused due to insufficient permissions.
75
The user may lack required object or field-level permissions.
76
"""
77
78
class SalesforceResourceNotFound(SalesforceError):
79
"""
80
Error Code 404: Not Found
81
82
Raised when the requested resource, record, or endpoint does not exist.
83
Verify record IDs, object names, and API endpoints.
84
"""
85
```
86
87
## Authentication and General Exceptions
88
89
Specialized exceptions for authentication failures and general Salesforce errors.
90
91
```python { .api }
92
class SalesforceAuthenticationFailed(SalesforceError):
93
"""
94
Authentication failure exception.
95
96
Raised during login when credentials are invalid, security tokens are wrong,
97
or authentication methods are not properly configured.
98
"""
99
100
class SalesforceGeneralError(SalesforceError):
101
"""
102
General Salesforce error for unspecified issues.
103
104
Raised for Salesforce-specific errors that don't fit other categories,
105
such as org limits, configuration issues, or service unavailability.
106
"""
107
```
108
109
## Bulk API v2.0 Exceptions
110
111
Specialized exceptions for Bulk API v2.0 operations with enhanced error details.
112
113
```python { .api }
114
class SalesforceOperationError(SalesforceError):
115
"""
116
Base exception for Bulk API v2.0 operation errors.
117
118
Provides additional context for bulk operation failures including
119
job information and processing details.
120
"""
121
122
class SalesforceBulkV2LoadError(SalesforceOperationError):
123
"""
124
Bulk API v2.0 data load/ingest error.
125
126
Raised when bulk insert, update, upsert, or delete operations fail.
127
Contains details about failed records and validation errors.
128
"""
129
130
class SalesforceBulkV2ExtractError(SalesforceOperationError):
131
"""
132
Bulk API v2.0 data extraction/query error.
133
134
Raised when bulk query operations fail due to query syntax errors,
135
resource limitations, or data access issues.
136
"""
137
```
138
139
## Exception Handling Patterns
140
141
### Basic Exception Handling
142
143
```python
144
from simple_salesforce import (
145
Salesforce,
146
SalesforceAuthenticationFailed,
147
SalesforceExpiredSession,
148
SalesforceResourceNotFound,
149
SalesforceMalformedRequest,
150
SalesforceRefusedRequest,
151
SalesforceError
152
)
153
154
try:
155
sf = Salesforce(
156
username='user@example.com',
157
password='password',
158
security_token='token'
159
)
160
161
# Perform operations
162
account = sf.Account.get('001XX000003DHPr')
163
164
except SalesforceAuthenticationFailed as e:
165
print(f"Authentication failed: {e}")
166
print("Check username, password, and security token")
167
168
except SalesforceExpiredSession as e:
169
print(f"Session expired: {e}")
170
print("Re-authenticate to get new session")
171
172
except SalesforceResourceNotFound as e:
173
print(f"Resource not found: {e}")
174
print("Verify the record ID exists")
175
176
except SalesforceMalformedRequest as e:
177
print(f"Bad request: {e}")
178
print("Check request parameters and data format")
179
180
except SalesforceRefusedRequest as e:
181
print(f"Access denied: {e}")
182
print("Check user permissions for this operation")
183
184
except SalesforceError as e:
185
print(f"General Salesforce error: {e}")
186
print(f"Status: {e.status}")
187
print(f"URL: {e.url}")
188
print(f"Content: {e.content}")
189
```
190
191
### Advanced Exception Analysis
192
193
```python
194
def analyze_salesforce_error(error):
195
"""
196
Analyze Salesforce error and provide detailed diagnostics.
197
198
Parameters:
199
- error: SalesforceError exception instance
200
201
Returns:
202
dict: Detailed error analysis and suggested actions
203
"""
204
205
analysis = {
206
'error_type': type(error).__name__,
207
'status_code': getattr(error, 'status', None),
208
'url': getattr(error, 'url', None),
209
'resource': getattr(error, 'resource_name', None),
210
'content': getattr(error, 'content', None),
211
'suggestions': []
212
}
213
214
# Parse error content for additional details
215
if hasattr(error, 'content') and error.content:
216
try:
217
import json
218
if isinstance(error.content, str):
219
content_data = json.loads(error.content)
220
else:
221
content_data = error.content
222
223
if isinstance(content_data, list) and len(content_data) > 0:
224
error_detail = content_data[0]
225
analysis['error_code'] = error_detail.get('errorCode')
226
analysis['message'] = error_detail.get('message')
227
analysis['fields'] = error_detail.get('fields', [])
228
except:
229
# Content is not JSON or not parseable
230
pass
231
232
# Add specific suggestions based on error type
233
if isinstance(error, SalesforceAuthenticationFailed):
234
analysis['suggestions'].extend([
235
"Verify username and password are correct",
236
"Check if security token is required and valid",
237
"Ensure the user account is not locked or deactivated",
238
"Verify the correct Salesforce domain (login vs test)"
239
])
240
241
elif isinstance(error, SalesforceExpiredSession):
242
analysis['suggestions'].extend([
243
"Re-authenticate to obtain a fresh session",
244
"Check session timeout settings",
245
"Implement session refresh logic for long-running processes"
246
])
247
248
elif isinstance(error, SalesforceResourceNotFound):
249
analysis['suggestions'].extend([
250
"Verify the record ID is valid and exists",
251
"Check if the object type is correct",
252
"Ensure the record hasn't been deleted",
253
"Verify API endpoint URLs are correct"
254
])
255
256
elif isinstance(error, SalesforceMalformedRequest):
257
analysis['suggestions'].extend([
258
"Check required fields are provided",
259
"Verify field names match the object schema",
260
"Ensure data types match field requirements",
261
"Check for invalid characters or formatting"
262
])
263
264
elif isinstance(error, SalesforceRefusedRequest):
265
analysis['suggestions'].extend([
266
"Check user has appropriate object permissions",
267
"Verify field-level security settings",
268
"Check if user has required profile or permission sets",
269
"Verify organization-wide defaults allow access"
270
])
271
272
return analysis
273
274
# Usage
275
try:
276
result = sf.Account.create({
277
'Name': '', # Empty required field
278
'InvalidField__c': 'value' # Non-existent field
279
})
280
except SalesforceError as e:
281
error_analysis = analyze_salesforce_error(e)
282
283
print(f"Error Type: {error_analysis['error_type']}")
284
print(f"Status: {error_analysis['status_code']}")
285
286
if 'message' in error_analysis:
287
print(f"Message: {error_analysis['message']}")
288
289
if error_analysis['suggestions']:
290
print("Suggestions:")
291
for suggestion in error_analysis['suggestions']:
292
print(f" - {suggestion}")
293
```
294
295
### Bulk Operations Error Handling
296
297
```python
298
def handle_bulk_errors(bulk_results, operation_type="bulk operation"):
299
"""
300
Handle and analyze bulk operation errors.
301
302
Parameters:
303
- bulk_results: Results from bulk operation
304
- operation_type: Description of the operation for logging
305
306
Returns:
307
dict: Error analysis and failed record details
308
"""
309
310
successful_records = []
311
failed_records = []
312
error_summary = {}
313
314
for i, result in enumerate(bulk_results):
315
if result.get('success', False):
316
successful_records.append(i)
317
else:
318
failed_records.append({
319
'index': i,
320
'error': result.get('error', 'Unknown error'),
321
'id': result.get('id'),
322
'result': result
323
})
324
325
# Categorize errors
326
error_msg = result.get('error', 'Unknown')
327
error_type = error_msg.split(':')[0] if ':' in error_msg else error_msg
328
error_summary[error_type] = error_summary.get(error_type, 0) + 1
329
330
success_rate = (len(successful_records) / len(bulk_results)) * 100 if bulk_results else 0
331
332
print(f"{operation_type.title()} Results:")
333
print(f" Total records: {len(bulk_results)}")
334
print(f" Successful: {len(successful_records)}")
335
print(f" Failed: {len(failed_records)}")
336
print(f" Success rate: {success_rate:.1f}%")
337
338
if error_summary:
339
print(" Error breakdown:")
340
for error_type, count in error_summary.items():
341
print(f" {error_type}: {count}")
342
343
return {
344
'success_count': len(successful_records),
345
'failure_count': len(failed_records),
346
'success_rate': success_rate,
347
'failed_records': failed_records,
348
'error_summary': error_summary
349
}
350
351
# Usage with bulk operations
352
try:
353
bulk_data = [
354
{'Name': 'Valid Account', 'Type': 'Customer'},
355
{'Name': '', 'Type': 'Customer'}, # Will fail - empty Name
356
{'Name': 'Another Valid Account', 'InvalidField': 'value'} # Will fail - invalid field
357
]
358
359
results = sf.bulk.Account.insert(bulk_data, include_detailed_results=True)
360
error_analysis = handle_bulk_errors(results, "bulk insert")
361
362
# Handle failed records
363
if error_analysis['failed_records']:
364
print("\nFailed record details:")
365
for failed in error_analysis['failed_records']:
366
print(f" Record {failed['index']}: {failed['error']}")
367
368
except SalesforceError as e:
369
print(f"Bulk operation failed entirely: {e}")
370
```
371
372
### Retry Logic with Exception Handling
373
374
```python
375
import time
376
import random
377
378
def retry_with_backoff(operation_func, max_retries=3, base_delay=1, max_delay=60):
379
"""
380
Retry operations with exponential backoff and exception handling.
381
382
Parameters:
383
- operation_func: Function to retry
384
- max_retries: Maximum number of retry attempts
385
- base_delay: Initial delay between retries
386
- max_delay: Maximum delay between retries
387
388
Returns:
389
Result of successful operation
390
391
Raises:
392
Last exception if all retries fail
393
"""
394
395
last_exception = None
396
397
for attempt in range(max_retries + 1):
398
try:
399
return operation_func()
400
401
except SalesforceExpiredSession as e:
402
print(f"Attempt {attempt + 1}: Session expired, re-authenticating...")
403
# Re-authentication would happen here
404
last_exception = e
405
406
except SalesforceRefusedRequest as e:
407
# Don't retry permission errors
408
print(f"Permission denied, not retrying: {e}")
409
raise
410
411
except SalesforceResourceNotFound as e:
412
# Don't retry for missing resources
413
print(f"Resource not found, not retrying: {e}")
414
raise
415
416
except (SalesforceMalformedRequest, SalesforceGeneralError) as e:
417
print(f"Attempt {attempt + 1}: Transient error, retrying... {e}")
418
last_exception = e
419
420
except SalesforceError as e:
421
print(f"Attempt {attempt + 1}: Unknown Salesforce error: {e}")
422
last_exception = e
423
424
# Calculate delay with exponential backoff and jitter
425
if attempt < max_retries:
426
delay = min(base_delay * (2 ** attempt), max_delay)
427
jitter = random.uniform(0, 0.1) * delay # Add up to 10% jitter
428
total_delay = delay + jitter
429
430
print(f"Waiting {total_delay:.1f} seconds before retry...")
431
time.sleep(total_delay)
432
433
# All retries exhausted
434
raise last_exception
435
436
# Usage
437
def risky_operation():
438
return sf.query("SELECT Id, Name FROM Account LIMIT 10")
439
440
try:
441
results = retry_with_backoff(risky_operation, max_retries=3)
442
print(f"Operation succeeded: {len(results['records'])} records")
443
444
except SalesforceError as e:
445
print(f"Operation failed after all retries: {e}")
446
```
447
448
### Context Manager for Error Handling
449
450
```python
451
from contextlib import contextmanager
452
453
@contextmanager
454
def salesforce_error_context(operation_name="Salesforce operation"):
455
"""
456
Context manager for comprehensive Salesforce error handling.
457
458
Parameters:
459
- operation_name: Name of the operation for logging
460
"""
461
462
try:
463
yield
464
465
except SalesforceAuthenticationFailed as e:
466
print(f"{operation_name} failed: Authentication error")
467
print("Suggestion: Check credentials and re-authenticate")
468
raise
469
470
except SalesforceExpiredSession as e:
471
print(f"{operation_name} failed: Session expired")
472
print("Suggestion: Refresh session and retry")
473
raise
474
475
except SalesforceResourceNotFound as e:
476
print(f"{operation_name} failed: Resource not found")
477
print(f"URL: {e.url}")
478
print("Suggestion: Verify record IDs and object names")
479
raise
480
481
except SalesforceMalformedRequest as e:
482
print(f"{operation_name} failed: Bad request")
483
print(f"Status: {e.status}")
484
if hasattr(e, 'content'):
485
print(f"Details: {e.content}")
486
print("Suggestion: Check request format and required fields")
487
raise
488
489
except SalesforceRefusedRequest as e:
490
print(f"{operation_name} failed: Permission denied")
491
print("Suggestion: Check user permissions and sharing settings")
492
raise
493
494
except SalesforceError as e:
495
print(f"{operation_name} failed: General Salesforce error")
496
print(f"Error type: {type(e).__name__}")
497
print(f"Status: {e.status}")
498
print(f"URL: {e.url}")
499
raise
500
501
except Exception as e:
502
print(f"{operation_name} failed: Unexpected error")
503
print(f"Error type: {type(e).__name__}")
504
print(f"Message: {str(e)}")
505
raise
506
507
# Usage
508
with salesforce_error_context("Account creation"):
509
new_account = sf.Account.create({
510
'Name': 'Test Account',
511
'Type': 'Customer'
512
})
513
print(f"Created account: {new_account['id']}")
514
515
with salesforce_error_context("Bulk insert operation"):
516
results = sf.bulk.Contact.insert(contact_data)
517
handle_bulk_errors(results, "contact insert")
518
```
519
520
## Error Prevention Best Practices
521
522
### Input Validation
523
524
```python
525
def validate_salesforce_input(data, object_type=None):
526
"""
527
Validate data before Salesforce operations to prevent errors.
528
529
Parameters:
530
- data: Record data dictionary or list of records
531
- object_type: Salesforce object type for schema validation
532
533
Returns:
534
dict: Validation results with errors and warnings
535
"""
536
537
errors = []
538
warnings = []
539
540
# Handle single record or list
541
records = data if isinstance(data, list) else [data]
542
543
for i, record in enumerate(records):
544
record_prefix = f"Record {i+1}: " if len(records) > 1 else ""
545
546
# Check for None values
547
if not record:
548
errors.append(f"{record_prefix}Empty record")
549
continue
550
551
# Check for required Name field (common requirement)
552
if 'Name' in record and not record['Name']:
553
errors.append(f"{record_prefix}Name field is empty")
554
555
# Check field name format (no spaces, proper case)
556
for field_name in record.keys():
557
if ' ' in field_name:
558
warnings.append(f"{record_prefix}Field '{field_name}' contains spaces")
559
560
# Check for potential typos in common fields
561
common_typos = {
562
'name': 'Name',
563
'type': 'Type',
564
'email': 'Email',
565
'phone': 'Phone'
566
}
567
568
if field_name.lower() in common_typos and field_name != common_typos[field_name.lower()]:
569
warnings.append(
570
f"{record_prefix}Field '{field_name}' might be '{common_typos[field_name.lower()]}'"
571
)
572
573
# Check for very long text values
574
for field_name, value in record.items():
575
if isinstance(value, str) and len(value) > 255:
576
warnings.append(
577
f"{record_prefix}Field '{field_name}' is very long ({len(value)} chars)"
578
)
579
580
return {
581
'valid': len(errors) == 0,
582
'errors': errors,
583
'warnings': warnings,
584
'record_count': len(records)
585
}
586
587
# Usage
588
account_data = {
589
'Name': 'Test Account',
590
'type': 'Customer', # Should be 'Type'
591
'Description': 'A' * 300 # Very long description
592
}
593
594
validation = validate_salesforce_input(account_data)
595
596
if not validation['valid']:
597
print("Validation errors:")
598
for error in validation['errors']:
599
print(f" {error}")
600
601
if validation['warnings']:
602
print("Validation warnings:")
603
for warning in validation['warnings']:
604
print(f" {warning}")
605
606
if validation['valid']:
607
# Proceed with operation
608
try:
609
result = sf.Account.create(account_data)
610
except SalesforceError as e:
611
error_analysis = analyze_salesforce_error(e)
612
```
613
614
### Proactive Error Detection
615
616
```python
617
def check_salesforce_connectivity(sf):
618
"""
619
Check Salesforce connectivity and basic functionality.
620
621
Parameters:
622
- sf: Salesforce client instance
623
624
Returns:
625
dict: Connectivity status and any issues found
626
"""
627
628
status = {
629
'connected': False,
630
'session_valid': False,
631
'api_accessible': False,
632
'errors': [],
633
'warnings': []
634
}
635
636
try:
637
# Test basic connectivity
638
limits = sf.limits()
639
status['connected'] = True
640
status['api_accessible'] = True
641
642
# Check API usage
643
if hasattr(sf, 'api_usage') and sf.api_usage:
644
if 'api-usage' in sf.api_usage:
645
usage = sf.api_usage['api-usage']
646
usage_percent = (usage.used / usage.total) * 100
647
648
if usage_percent > 90:
649
status['warnings'].append(f"API usage very high: {usage_percent:.1f}%")
650
elif usage_percent > 75:
651
status['warnings'].append(f"API usage high: {usage_percent:.1f}%")
652
653
# Test session validity
654
try:
655
sf.describe()
656
status['session_valid'] = True
657
except SalesforceExpiredSession:
658
status['errors'].append("Session has expired")
659
660
except SalesforceAuthenticationFailed:
661
status['errors'].append("Authentication failed")
662
except SalesforceError as e:
663
status['errors'].append(f"Salesforce error: {e}")
664
except Exception as e:
665
status['errors'].append(f"Connection error: {e}")
666
667
return status
668
669
# Usage
670
connectivity = check_salesforce_connectivity(sf)
671
672
if not connectivity['connected']:
673
print("Salesforce connectivity issues:")
674
for error in connectivity['errors']:
675
print(f" {error}")
676
else:
677
print("Salesforce connection OK")
678
679
if connectivity['warnings']:
680
print("Warnings:")
681
for warning in connectivity['warnings']:
682
print(f" {warning}")
683
```