or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

account-management.mdcryptographic-primitives.mderror-handling.mdindex.mdnetwork-sysvars.mdrpc-functionality.mdsystem-programs.mdtesting-infrastructure.mdtoken-operations.mdtransaction-construction.mdtransaction-status.md

error-handling.mddocs/

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"}]