0
# Error Handling
1
2
Comprehensive error types for transaction processing, serialization, signing operations, and RPC communications. This provides detailed error classification and handling strategies for building robust Solana applications with proper error recovery and user feedback.
3
4
## Capabilities
5
6
### Core Exception Classes
7
8
Foundation exception classes for different categories of errors in Solana operations.
9
10
```python { .api }
11
class SignerError(Exception):
12
"""
13
Errors during transaction signing operations.
14
15
Raised when:
16
- Private key operations fail
17
- Signature verification fails
18
- Key format is invalid
19
- Cryptographic operations encounter errors
20
"""
21
def __init__(self, message: str):
22
"""
23
Create signer error.
24
25
Parameters:
26
- message: str, error description
27
"""
28
super().__init__(message)
29
30
class BincodeError(Exception):
31
"""
32
Binary encoding/decoding errors using bincode format.
33
34
Raised when:
35
- Serialization fails due to invalid data structures
36
- Deserialization encounters malformed binary data
37
- Data size constraints are violated
38
- Type conversion errors occur during encoding/decoding
39
"""
40
def __init__(self, message: str):
41
"""
42
Create bincode error.
43
44
Parameters:
45
- message: str, error description
46
"""
47
super().__init__(message)
48
49
class CborError(Exception):
50
"""
51
CBOR (Concise Binary Object Representation) encoding/decoding errors.
52
53
Raised when:
54
- CBOR format is invalid or corrupted
55
- Unsupported data types are encountered
56
- Encoding constraints are exceeded
57
- Decoding fails due to malformed input
58
"""
59
def __init__(self, message: str):
60
"""
61
Create CBOR error.
62
63
Parameters:
64
- message: str, error description
65
"""
66
super().__init__(message)
67
68
class SerdeJSONError(Exception):
69
"""
70
JSON serialization/deserialization errors.
71
72
Raised when:
73
- JSON parsing fails due to invalid syntax
74
- Required fields are missing during deserialization
75
- Type conversion fails between JSON and Rust/Python types
76
- JSON structure doesn't match expected schema
77
"""
78
def __init__(self, message: str):
79
"""
80
Create JSON serde error.
81
82
Parameters:
83
- message: str, error description
84
"""
85
super().__init__(message)
86
```
87
88
### Transaction-Specific Errors
89
90
Errors related to transaction construction, validation, and execution.
91
92
```python { .api }
93
class SanitizeError(Exception):
94
"""
95
Transaction sanitization and validation errors.
96
97
Raised when:
98
- Transaction format is invalid
99
- Required signatures are missing
100
- Account references are invalid
101
- Message structure is malformed
102
- Instruction data is inconsistent
103
"""
104
def __init__(self, message: str):
105
"""
106
Create sanitization error.
107
108
Parameters:
109
- message: str, validation error description
110
"""
111
super().__init__(message)
112
113
class TransactionError(Exception):
114
"""
115
General transaction processing errors.
116
117
Raised when:
118
- Transaction execution fails
119
- Network submission errors occur
120
- Fee calculation problems arise
121
- Account state conflicts prevent execution
122
"""
123
def __init__(self, message: str):
124
"""
125
Create transaction error.
126
127
Parameters:
128
- message: str, transaction error description
129
"""
130
super().__init__(message)
131
132
class CompileError(Exception):
133
"""
134
Message compilation errors, especially with address lookup tables.
135
136
Raised when:
137
- Address lookup table compilation fails
138
- Message size exceeds limits
139
- Account key resolution fails
140
- Versioned message construction errors
141
"""
142
def __init__(self, message: str):
143
"""
144
Create compilation error.
145
146
Parameters:
147
- message: str, compilation error description
148
"""
149
super().__init__(message)
150
```
151
152
### Cryptographic Errors
153
154
Errors related to cryptographic operations and key management.
155
156
```python { .api }
157
class ParseHashError(Exception):
158
"""
159
Hash parsing and validation errors.
160
161
Raised when:
162
- Base58 hash string is invalid
163
- Hash length is incorrect (not 32 bytes)
164
- Hash format doesn't match expected pattern
165
- Conversion between hash formats fails
166
"""
167
def __init__(self, message: str):
168
"""
169
Create hash parsing error.
170
171
Parameters:
172
- message: str, hash parsing error description
173
"""
174
super().__init__(message)
175
176
class KeypairError(Exception):
177
"""
178
Keypair generation and management errors.
179
180
Raised when:
181
- Key generation fails
182
- Seed format is invalid
183
- Private key operations fail
184
- Key derivation encounters errors
185
"""
186
def __init__(self, message: str):
187
"""
188
Create keypair error.
189
190
Parameters:
191
- message: str, keypair error description
192
"""
193
super().__init__(message)
194
195
class SignatureError(Exception):
196
"""
197
Digital signature operation errors.
198
199
Raised when:
200
- Signature verification fails
201
- Signature format is invalid
202
- Signing operation encounters cryptographic errors
203
- Public key doesn't match signature
204
"""
205
def __init__(self, message: str):
206
"""
207
Create signature error.
208
209
Parameters:
210
- message: str, signature error description
211
"""
212
super().__init__(message)
213
```
214
215
### RPC Communication Errors
216
217
Errors related to network communication and RPC operations.
218
219
```python { .api }
220
class RpcError(Exception):
221
"""
222
RPC communication and response errors.
223
224
Base class for all RPC-related errors including network issues,
225
server errors, and response parsing problems.
226
"""
227
def __init__(self, message: str, error_code: Optional[int] = None):
228
"""
229
Create RPC error.
230
231
Parameters:
232
- message: str, error description
233
- error_code: Optional[int], RPC error code if available
234
"""
235
super().__init__(message)
236
self.error_code = error_code
237
238
class RpcConnectionError(RpcError):
239
"""
240
Network connection errors for RPC communication.
241
242
Raised when:
243
- Network connection fails
244
- Timeout occurs during request
245
- Connection is refused or dropped
246
- DNS resolution fails
247
"""
248
pass
249
250
class RpcResponseError(RpcError):
251
"""
252
RPC response parsing and validation errors.
253
254
Raised when:
255
- Response JSON is malformed
256
- Response structure doesn't match expected format
257
- Required response fields are missing
258
- Response data type conversion fails
259
"""
260
pass
261
262
class RpcServerError(RpcError):
263
"""
264
Server-side RPC errors returned by Solana nodes.
265
266
Raised when:
267
- RPC method is not supported
268
- Request parameters are invalid
269
- Server encounters internal errors
270
- Rate limiting is applied
271
"""
272
pass
273
```
274
275
### Account and Program Errors
276
277
Errors related to account operations and program execution.
278
279
```python { .api }
280
class AccountError(Exception):
281
"""
282
Account data and state management errors.
283
284
Raised when:
285
- Account doesn't exist
286
- Account data format is invalid
287
- Account ownership checks fail
288
- Account size constraints are violated
289
"""
290
def __init__(self, message: str, account: Optional[Pubkey] = None):
291
"""
292
Create account error.
293
294
Parameters:
295
- message: str, error description
296
- account: Optional[Pubkey], account that caused the error
297
"""
298
super().__init__(message)
299
self.account = account
300
301
class ProgramError(Exception):
302
"""
303
Program execution and deployment errors.
304
305
Raised when:
306
- Program doesn't exist or is invalid
307
- Program execution fails
308
- Custom program errors occur
309
- Program account state is inconsistent
310
"""
311
def __init__(self, message: str, program_id: Optional[Pubkey] = None):
312
"""
313
Create program error.
314
315
Parameters:
316
- message: str, error description
317
- program_id: Optional[Pubkey], program that caused the error
318
"""
319
super().__init__(message)
320
self.program_id = program_id
321
322
class InstructionError(Exception):
323
"""
324
Instruction execution and validation errors.
325
326
Raised when:
327
- Instruction data is invalid
328
- Required accounts are missing
329
- Account permissions are insufficient
330
- Instruction-specific validation fails
331
"""
332
def __init__(self, message: str, instruction_index: Optional[int] = None):
333
"""
334
Create instruction error.
335
336
Parameters:
337
- message: str, error description
338
- instruction_index: Optional[int], index of failing instruction
339
"""
340
super().__init__(message)
341
self.instruction_index = instruction_index
342
```
343
344
### Token Program Errors
345
346
Errors specific to SPL Token operations and state management.
347
348
```python { .api }
349
class TokenError(Exception):
350
"""
351
SPL Token program operation errors.
352
353
Raised when:
354
- Token account operations fail
355
- Mint operations are invalid
356
- Token transfer constraints are violated
357
- Authority checks fail
358
"""
359
def __init__(self, message: str, mint: Optional[Pubkey] = None):
360
"""
361
Create token error.
362
363
Parameters:
364
- message: str, error description
365
- mint: Optional[Pubkey], token mint that caused the error
366
"""
367
super().__init__(message)
368
self.mint = mint
369
370
class TokenAccountError(TokenError):
371
"""
372
Token account specific errors.
373
374
Raised when:
375
- Token account is frozen
376
- Insufficient token balance
377
- Account owner mismatch
378
- Account is not initialized
379
"""
380
def __init__(self, message: str, token_account: Optional[Pubkey] = None):
381
"""
382
Create token account error.
383
384
Parameters:
385
- message: str, error description
386
- token_account: Optional[Pubkey], token account that caused the error
387
"""
388
super().__init__(message)
389
self.token_account = token_account
390
391
class TokenMintError(TokenError):
392
"""
393
Token mint specific errors.
394
395
Raised when:
396
- Mint authority is invalid
397
- Supply constraints are violated
398
- Mint is frozen or disabled
399
- Decimal precision errors occur
400
"""
401
pass
402
```
403
404
## Usage Examples
405
406
### Basic Error Handling
407
408
```python
409
from solders.errors import (
410
SignerError, BincodeError, SanitizeError, TransactionError, ParseHashError
411
)
412
from solders.keypair import Keypair
413
from solders.hash import Hash
414
from solders.transaction import Transaction
415
416
def safe_keypair_operations():
417
"""Demonstrate safe keypair operations with error handling."""
418
419
try:
420
# Generate keypair
421
keypair = Keypair()
422
print(f"Generated keypair: {keypair.pubkey()}")
423
424
except SignerError as e:
425
print(f"Keypair generation failed: {e}")
426
return None
427
428
try:
429
# Create from seed
430
seed = b"my_seed_exactly_32_bytes_long!!"
431
deterministic_keypair = Keypair.from_seed(seed)
432
print(f"Deterministic keypair: {deterministic_keypair.pubkey()}")
433
434
except (SignerError, ValueError) as e:
435
print(f"Seed-based keypair creation failed: {e}")
436
437
try:
438
# Invalid seed (wrong length)
439
invalid_seed = b"too_short"
440
Keypair.from_seed(invalid_seed)
441
442
except ValueError as e:
443
print(f"Expected error with invalid seed: {e}")
444
445
return keypair
446
447
def safe_hash_operations():
448
"""Demonstrate safe hash operations with error handling."""
449
450
try:
451
# Valid hash creation
452
valid_hash = Hash.from_string("11111111111111111111111111111112")
453
print(f"Valid hash: {valid_hash}")
454
455
except ParseHashError as e:
456
print(f"Hash parsing failed: {e}")
457
458
try:
459
# Invalid hash string
460
Hash.from_string("invalid_hash_string")
461
462
except ParseHashError as e:
463
print(f"Expected error with invalid hash: {e}")
464
465
try:
466
# Wrong length bytes
467
Hash(b"too_short")
468
469
except ValueError as e:
470
print(f"Expected error with wrong length: {e}")
471
```
472
473
### Transaction Error Handling
474
475
```python
476
def safe_transaction_construction():
477
"""Demonstrate safe transaction construction with comprehensive error handling."""
478
479
try:
480
keypair = Keypair()
481
482
# Create instruction (this could fail)
483
from solders.system_program import transfer, TransferParams
484
from solders.instruction import Instruction, AccountMeta
485
486
transfer_ix = transfer(TransferParams(
487
from_pubkey=keypair.pubkey(),
488
to_pubkey=Keypair().pubkey(),
489
lamports=1000000
490
))
491
492
# Create transaction
493
transaction = Transaction.new_with_payer([transfer_ix], keypair.pubkey())
494
495
# Set recent blockhash
496
recent_blockhash = Hash.default() # In real usage, get from RPC
497
498
# Sign transaction (this could fail)
499
transaction.sign([keypair], recent_blockhash)
500
501
print("Transaction constructed and signed successfully")
502
return transaction
503
504
except SanitizeError as e:
505
print(f"Transaction validation failed: {e}")
506
return None
507
except SignerError as e:
508
print(f"Transaction signing failed: {e}")
509
return None
510
except TransactionError as e:
511
print(f"General transaction error: {e}")
512
return None
513
except Exception as e:
514
print(f"Unexpected error during transaction construction: {e}")
515
return None
516
517
def handle_transaction_simulation_errors(svm, transaction):
518
"""Handle errors during transaction simulation."""
519
520
try:
521
from solders.litesvm import LiteSVM
522
523
# Simulate transaction
524
result = svm.simulate_transaction(transaction)
525
526
# Check if simulation succeeded
527
from solders.transaction_metadata import SimulatedTransactionInfo, FailedTransactionMetadata
528
529
if isinstance(result, SimulatedTransactionInfo):
530
print("Simulation successful!")
531
print(f"Compute units: {result.compute_units_consumed}")
532
return True
533
else:
534
print(f"Simulation failed: {result.error}")
535
return False
536
537
except Exception as e:
538
print(f"Simulation error: {e}")
539
return False
540
```
541
542
### RPC Error Handling
543
544
```python
545
from solders.rpc.requests import GetAccountInfo, GetBalance
546
from solders.rpc.config import RpcAccountInfoConfig
547
from solders.commitment_config import CommitmentConfig
548
549
def safe_rpc_operations():
550
"""Demonstrate safe RPC operations with comprehensive error handling."""
551
552
# This is a mock example - in real usage you'd have an actual RPC client
553
def mock_rpc_call(request):
554
# Simulate different types of RPC errors
555
import random
556
error_type = random.choice(['success', 'connection', 'server', 'response'])
557
558
if error_type == 'connection':
559
raise RpcConnectionError("Connection timeout")
560
elif error_type == 'server':
561
raise RpcServerError("Internal server error", error_code=500)
562
elif error_type == 'response':
563
raise RpcResponseError("Invalid JSON response")
564
else:
565
return {"jsonrpc": "2.0", "result": {"value": None}, "id": 1}
566
567
def get_account_info_safely(pubkey):
568
"""Safely get account info with proper error handling."""
569
570
try:
571
# Create request
572
config = RpcAccountInfoConfig(
573
commitment=CommitmentConfig.confirmed()
574
)
575
request = GetAccountInfo(pubkey, config)
576
577
# Make RPC call
578
response = mock_rpc_call(request)
579
580
# Process response
581
if response["result"]["value"] is None:
582
print(f"Account {pubkey} does not exist")
583
return None
584
else:
585
print(f"Account {pubkey} found")
586
return response["result"]["value"]
587
588
except RpcConnectionError as e:
589
print(f"Network connection failed: {e}")
590
# Implement retry logic here
591
return None
592
except RpcServerError as e:
593
print(f"Server error (code {e.error_code}): {e}")
594
# May want to retry with exponential backoff
595
return None
596
except RpcResponseError as e:
597
print(f"Response parsing failed: {e}")
598
# Response format issue, likely permanent
599
return None
600
except Exception as e:
601
print(f"Unexpected RPC error: {e}")
602
return None
603
604
# Test the function
605
test_pubkey = Keypair().pubkey()
606
account_info = get_account_info_safely(test_pubkey)
607
608
return account_info
609
610
def implement_rpc_retry_logic():
611
"""Implement retry logic for RPC operations."""
612
import time
613
import random
614
615
def retry_rpc_call(request, max_retries=3, base_delay=1.0):
616
"""Retry RPC calls with exponential backoff."""
617
618
last_error = None
619
620
for attempt in range(max_retries + 1):
621
try:
622
# Mock RPC call that might fail
623
if random.random() < 0.7: # 70% failure rate for demo
624
raise RpcConnectionError("Connection failed")
625
626
return {"success": True, "attempt": attempt}
627
628
except RpcConnectionError as e:
629
last_error = e
630
if attempt < max_retries:
631
delay = base_delay * (2 ** attempt) + random.uniform(0, 1)
632
print(f"Attempt {attempt + 1} failed, retrying in {delay:.2f}s...")
633
time.sleep(delay)
634
else:
635
print(f"All {max_retries + 1} attempts failed")
636
except RpcServerError as e:
637
# Don't retry server errors with 5xx codes
638
if e.error_code and 500 <= e.error_code < 600:
639
print(f"Server error {e.error_code}, not retrying")
640
raise e
641
last_error = e
642
except RpcResponseError as e:
643
# Don't retry response parsing errors
644
print(f"Response error, not retrying: {e}")
645
raise e
646
647
# All retries exhausted
648
raise last_error
649
650
# Test retry logic
651
try:
652
result = retry_rpc_call("test_request")
653
print(f"Success after {result['attempt'] + 1} attempts")
654
except Exception as e:
655
print(f"Final failure: {e}")
656
```
657
658
### Token Operation Error Handling
659
660
```python
661
def safe_token_operations():
662
"""Demonstrate safe token operations with error handling."""
663
664
def parse_token_account_safely(account_data):
665
"""Safely parse token account data."""
666
667
try:
668
from solders.token.state import TokenAccount, TokenAccountState
669
670
# Check data length
671
if len(account_data) != 165:
672
raise TokenAccountError(f"Invalid token account size: {len(account_data)} bytes")
673
674
# Parse token account
675
token_account = TokenAccount.deserialize(account_data)
676
677
# Validate state
678
if token_account.state == TokenAccountState.Uninitialized:
679
raise TokenAccountError("Token account is not initialized")
680
elif token_account.state == TokenAccountState.Frozen:
681
raise TokenAccountError("Token account is frozen")
682
683
return token_account
684
685
except TokenAccountError as e:
686
print(f"Token account error: {e}")
687
return None
688
except ValueError as e:
689
print(f"Token account parsing failed: {e}")
690
return None
691
except Exception as e:
692
print(f"Unexpected error parsing token account: {e}")
693
return None
694
695
def validate_token_transfer(source_account, amount, owner):
696
"""Validate token transfer before execution."""
697
698
try:
699
# Check account state
700
if not source_account:
701
raise TokenAccountError("Source account not found")
702
703
if source_account.state != TokenAccountState.Initialized:
704
raise TokenAccountError("Source account not initialized")
705
706
if source_account.state == TokenAccountState.Frozen:
707
raise TokenAccountError("Source account is frozen")
708
709
# Check balance
710
if source_account.amount < amount:
711
raise TokenAccountError(
712
f"Insufficient balance: {source_account.amount} < {amount}"
713
)
714
715
# Check authority
716
if source_account.owner != owner and source_account.delegate != owner:
717
raise TokenAccountError("Not authorized to transfer from this account")
718
719
# Check delegate limits
720
if source_account.delegate == owner:
721
if source_account.delegated_amount < amount:
722
raise TokenAccountError(
723
f"Amount exceeds delegated limit: {amount} > {source_account.delegated_amount}"
724
)
725
726
return True
727
728
except TokenAccountError as e:
729
print(f"Token transfer validation failed: {e}")
730
return False
731
732
# Example usage
733
mock_account_data = bytes(165) # Mock token account data
734
token_account = parse_token_account_safely(mock_account_data)
735
736
if token_account:
737
is_valid = validate_token_transfer(token_account, 100, Keypair().pubkey())
738
print(f"Transfer validation result: {is_valid}")
739
```
740
741
### Error Recovery Strategies
742
743
```python
744
class ErrorRecoveryManager:
745
"""Manager for implementing error recovery strategies."""
746
747
def __init__(self):
748
self.retry_counts = {}
749
self.error_history = []
750
751
def handle_transaction_error(self, error, transaction, context):
752
"""Handle transaction errors with appropriate recovery strategy."""
753
754
self.error_history.append({
755
'error': str(error),
756
'type': type(error).__name__,
757
'timestamp': time.time(),
758
'context': context
759
})
760
761
if isinstance(error, SanitizeError):
762
return self._handle_sanitize_error(error, transaction)
763
elif isinstance(error, SignerError):
764
return self._handle_signer_error(error, transaction)
765
elif isinstance(error, TransactionError):
766
return self._handle_transaction_error(error, transaction)
767
else:
768
return self._handle_unknown_error(error, transaction)
769
770
def _handle_sanitize_error(self, error, transaction):
771
"""Handle transaction sanitization errors."""
772
print(f"Transaction validation failed: {error}")
773
774
# Common fixes for sanitization errors
775
suggestions = [
776
"Check that all required signers are present",
777
"Verify account references are valid",
778
"Ensure instruction data is properly formatted",
779
"Check that recent blockhash is set"
780
]
781
782
print("Possible fixes:")
783
for suggestion in suggestions:
784
print(f" - {suggestion}")
785
786
return False # Cannot auto-recover from sanitization errors
787
788
def _handle_signer_error(self, error, transaction):
789
"""Handle signing errors."""
790
print(f"Signing failed: {error}")
791
792
# Signing errors usually require developer intervention
793
suggestions = [
794
"Verify all required keypairs are available",
795
"Check that keypair private keys are valid",
796
"Ensure transaction is not already signed",
797
"Validate signature order matches account order"
798
]
799
800
print("Possible fixes:")
801
for suggestion in suggestions:
802
print(f" - {suggestion}")
803
804
return False
805
806
def _handle_transaction_error(self, error, transaction):
807
"""Handle general transaction errors."""
808
print(f"Transaction execution failed: {error}")
809
810
# Some transaction errors might be recoverable
811
error_str = str(error).lower()
812
813
if "insufficient funds" in error_str:
814
print("Recovery suggestion: Add more SOL to fee payer account")
815
return False
816
elif "account not found" in error_str:
817
print("Recovery suggestion: Ensure all referenced accounts exist")
818
return False
819
elif "blockhash not found" in error_str:
820
print("Recovery suggestion: Update recent blockhash and retry")
821
return True # This is recoverable
822
else:
823
print("Unknown transaction error - manual investigation required")
824
return False
825
826
def _handle_unknown_error(self, error, transaction):
827
"""Handle unexpected errors."""
828
print(f"Unexpected error: {error}")
829
830
# Log for investigation
831
print("This error should be investigated and proper handling added")
832
833
return False
834
835
def get_error_summary(self):
836
"""Get summary of encountered errors."""
837
if not self.error_history:
838
return "No errors recorded"
839
840
error_counts = {}
841
for error_record in self.error_history:
842
error_type = error_record['type']
843
error_counts[error_type] = error_counts.get(error_type, 0) + 1
844
845
summary = "Error Summary:\n"
846
for error_type, count in error_counts.items():
847
summary += f" {error_type}: {count} occurrences\n"
848
849
return summary
850
851
def demonstrate_error_recovery():
852
"""Demonstrate error recovery strategies."""
853
854
recovery_manager = ErrorRecoveryManager()
855
856
# Simulate various errors
857
errors = [
858
SanitizeError("Missing required signature"),
859
SignerError("Invalid private key"),
860
TransactionError("Blockhash not found"),
861
ParseHashError("Invalid hash format")
862
]
863
864
for error in errors:
865
print(f"\n--- Handling {type(error).__name__} ---")
866
recoverable = recovery_manager.handle_transaction_error(
867
error, None, {"test": True}
868
)
869
print(f"Recoverable: {recoverable}")
870
871
print("\n" + recovery_manager.get_error_summary())
872
```
873
874
### Custom Error Classes
875
876
```python
877
class SolanaApplicationError(Exception):
878
"""Base class for application-specific Solana errors."""
879
880
def __init__(self, message, error_code=None, context=None):
881
super().__init__(message)
882
self.error_code = error_code
883
self.context = context or {}
884
885
def to_dict(self):
886
"""Convert error to dictionary for logging/serialization."""
887
return {
888
'error_type': self.__class__.__name__,
889
'message': str(self),
890
'error_code': self.error_code,
891
'context': self.context
892
}
893
894
class InsufficientBalanceError(SolanaApplicationError):
895
"""Raised when account has insufficient balance for operation."""
896
897
def __init__(self, required, available, account=None):
898
message = f"Insufficient balance: need {required}, have {available}"
899
super().__init__(
900
message,
901
error_code="INSUFFICIENT_BALANCE",
902
context={
903
'required': required,
904
'available': available,
905
'account': str(account) if account else None
906
}
907
)
908
909
class AccountNotFoundError(SolanaApplicationError):
910
"""Raised when required account is not found."""
911
912
def __init__(self, account_pubkey):
913
message = f"Account not found: {account_pubkey}"
914
super().__init__(
915
message,
916
error_code="ACCOUNT_NOT_FOUND",
917
context={'account': str(account_pubkey)}
918
)
919
920
class InvalidTokenOperationError(SolanaApplicationError):
921
"""Raised when token operation is invalid."""
922
923
def __init__(self, operation, reason, token_account=None):
924
message = f"Invalid {operation}: {reason}"
925
super().__init__(
926
message,
927
error_code="INVALID_TOKEN_OPERATION",
928
context={
929
'operation': operation,
930
'reason': reason,
931
'token_account': str(token_account) if token_account else None
932
}
933
)
934
935
def use_custom_errors():
936
"""Example using custom error classes."""
937
938
try:
939
# Simulate balance check
940
required_amount = 5_000_000_000 # 5 SOL
941
available_amount = 2_000_000_000 # 2 SOL
942
943
if available_amount < required_amount:
944
raise InsufficientBalanceError(
945
required=required_amount,
946
available=available_amount,
947
account=Keypair().pubkey()
948
)
949
950
except InsufficientBalanceError as e:
951
print(f"Balance error: {e}")
952
print(f"Error details: {e.to_dict()}")
953
954
try:
955
# Simulate account lookup
956
account_pubkey = Keypair().pubkey()
957
# account = get_account(account_pubkey) # Returns None
958
959
if True: # Simulate not found
960
raise AccountNotFoundError(account_pubkey)
961
962
except AccountNotFoundError as e:
963
print(f"Account error: {e}")
964
print(f"Error code: {e.error_code}")
965
```
966
967
## Error Logging and Monitoring
968
969
### Error Logging Utilities
970
971
```python
972
import logging
973
import json
974
from datetime import datetime
975
976
class SolanaErrorLogger:
977
"""Structured logging for Solana application errors."""
978
979
def __init__(self, logger_name="solana_app"):
980
self.logger = logging.getLogger(logger_name)
981
self.logger.setLevel(logging.INFO)
982
983
# Create structured formatter
984
formatter = logging.Formatter(
985
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
986
)
987
988
# Add handler if none exists
989
if not self.logger.handlers:
990
handler = logging.StreamHandler()
991
handler.setFormatter(formatter)
992
self.logger.addHandler(handler)
993
994
def log_error(self, error, context=None):
995
"""Log error with structured context."""
996
997
error_data = {
998
'error_type': type(error).__name__,
999
'error_message': str(error),
1000
'timestamp': datetime.utcnow().isoformat(),
1001
'context': context or {}
1002
}
1003
1004
# Add error-specific context
1005
if hasattr(error, 'error_code'):
1006
error_data['error_code'] = error.error_code
1007
1008
if hasattr(error, 'account'):
1009
error_data['account'] = str(error.account)
1010
1011
if hasattr(error, 'instruction_index'):
1012
error_data['instruction_index'] = error.instruction_index
1013
1014
self.logger.error(json.dumps(error_data))
1015
1016
def log_transaction_error(self, error, transaction_signature=None):
1017
"""Log transaction-specific errors."""
1018
1019
context = {
1020
'component': 'transaction_processing'
1021
}
1022
1023
if transaction_signature:
1024
context['transaction_signature'] = str(transaction_signature)
1025
1026
self.log_error(error, context)
1027
1028
def log_rpc_error(self, error, method=None, endpoint=None):
1029
"""Log RPC-specific errors."""
1030
1031
context = {
1032
'component': 'rpc_communication',
1033
'method': method,
1034
'endpoint': endpoint
1035
}
1036
1037
self.log_error(error, context)
1038
1039
# Usage example
1040
error_logger = SolanaErrorLogger()
1041
1042
def example_with_logging():
1043
"""Example function with comprehensive error logging."""
1044
1045
try:
1046
# Simulate some operation that might fail
1047
raise TransactionError("Simulation failed")
1048
1049
except TransactionError as e:
1050
error_logger.log_transaction_error(
1051
e,
1052
transaction_signature="5j7s1QjzGfTgxYhNqgX4p8Q2s3YqHjK..."
1053
)
1054
1055
try:
1056
# Simulate RPC error
1057
raise RpcConnectionError("Connection timeout")
1058
1059
except RpcConnectionError as e:
1060
error_logger.log_rpc_error(
1061
e,
1062
method="getAccountInfo",
1063
endpoint="https://api.mainnet-beta.solana.com"
1064
)
1065
```
1066
1067
### Error Metrics and Monitoring
1068
1069
```python
1070
class ErrorMetrics:
1071
"""Track error metrics for monitoring and alerting."""
1072
1073
def __init__(self):
1074
self.error_counts = {}
1075
self.error_rates = {}
1076
self.last_reset = time.time()
1077
1078
def record_error(self, error_type):
1079
"""Record occurrence of error type."""
1080
self.error_counts[error_type] = self.error_counts.get(error_type, 0) + 1
1081
1082
def get_error_rate(self, error_type, window_seconds=3600):
1083
"""Calculate error rate for given time window."""
1084
current_time = time.time()
1085
1086
if error_type not in self.error_rates:
1087
self.error_rates[error_type] = []
1088
1089
# Add current error
1090
self.error_rates[error_type].append(current_time)
1091
1092
# Remove old errors outside window
1093
cutoff = current_time - window_seconds
1094
self.error_rates[error_type] = [
1095
t for t in self.error_rates[error_type] if t > cutoff
1096
]
1097
1098
return len(self.error_rates[error_type]) / window_seconds
1099
1100
def get_summary(self):
1101
"""Get error summary for monitoring dashboard."""
1102
total_errors = sum(self.error_counts.values())
1103
1104
summary = {
1105
'total_errors': total_errors,
1106
'error_breakdown': dict(self.error_counts),
1107
'uptime_seconds': time.time() - self.last_reset
1108
}
1109
1110
# Calculate rates
1111
rates = {}
1112
for error_type in self.error_counts:
1113
rates[error_type] = self.get_error_rate(error_type)
1114
1115
summary['error_rates_per_second'] = rates
1116
1117
return summary
1118
1119
def reset_metrics(self):
1120
"""Reset all metrics."""
1121
self.error_counts.clear()
1122
self.error_rates.clear()
1123
self.last_reset = time.time()
1124
1125
# Global metrics instance
1126
error_metrics = ErrorMetrics()
1127
1128
def monitored_operation():
1129
"""Example operation with error monitoring."""
1130
1131
try:
1132
# Some operation that might fail
1133
import random
1134
if random.random() < 0.3:
1135
raise SignerError("Random signing failure")
1136
1137
print("Operation successful")
1138
1139
except SignerError as e:
1140
error_metrics.record_error('SignerError')
1141
error_logger.log_error(e, {'operation': 'monitored_operation'})
1142
raise
1143
except Exception as e:
1144
error_metrics.record_error('UnknownError')
1145
error_logger.log_error(e, {'operation': 'monitored_operation'})
1146
raise
1147
1148
# Example monitoring
1149
def print_error_metrics():
1150
"""Print current error metrics."""
1151
summary = error_metrics.get_summary()
1152
1153
print("=== Error Metrics ===")
1154
print(f"Total errors: {summary['total_errors']}")
1155
print(f"Uptime: {summary['uptime_seconds']:.0f} seconds")
1156
1157
print("\nError breakdown:")
1158
for error_type, count in summary['error_breakdown'].items():
1159
rate = summary['error_rates_per_second'].get(error_type, 0)
1160
print(f" {error_type}: {count} total ({rate:.4f}/sec)")
1161
```
1162
1163
This completes the comprehensive Knowledge Tile documentation for the solders Python package. The documentation now covers all major functional areas with detailed API references, practical usage examples, and proper error handling guidance.
1164
1165
<function_calls>
1166
<invoke name="TodoWrite">
1167
<parameter name="todos">[{"content": "Create main entry-point documentation (index.md)", "status": "completed", "activeForm": "Creating main entry-point documentation"}, {"content": "Create blockchain primitives sub-doc (cryptographic-primitives.md)", "status": "completed", "activeForm": "Creating blockchain primitives sub-doc"}, {"content": "Create transaction construction sub-doc (transaction-construction.md)", "status": "completed", "activeForm": "Creating transaction construction sub-doc"}, {"content": "Create account management sub-doc (account-management.md)", "status": "completed", "activeForm": "Creating account management sub-doc"}, {"content": "Create system programs sub-doc (system-programs.md)", "status": "completed", "activeForm": "Creating system programs sub-doc"}, {"content": "Create token operations sub-doc (token-operations.md)", "status": "completed", "activeForm": "Creating token operations sub-doc"}, {"content": "Create RPC functionality sub-doc (rpc-functionality.md)", "status": "completed", "activeForm": "Creating RPC functionality sub-doc"}, {"content": "Create network and sysvars sub-doc (network-sysvars.md)", "status": "completed", "activeForm": "Creating network and sysvars sub-doc"}, {"content": "Create transaction status sub-doc (transaction-status.md)", "status": "completed", "activeForm": "Creating transaction status sub-doc"}, {"content": "Create testing infrastructure sub-doc (testing-infrastructure.md)", "status": "completed", "activeForm": "Creating testing infrastructure sub-doc"}, {"content": "Create error handling sub-doc (error-handling.md)", "status": "completed", "activeForm": "Creating error handling sub-doc"}]