or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

asgi-middleware.mdasync-client.mdasync-server.mdclient.mdexceptions.mdindex.mdserver.mdwsgi-middleware.md

exceptions.mddocs/

0

# Exception Handling

1

2

Comprehensive exception system for handling Engine.IO-specific errors and connection issues. All exceptions inherit from the base `EngineIOError` class for consistent error handling patterns.

3

4

## Capabilities

5

6

### Base Exception Class

7

8

Foundation exception class for all Engine.IO-related errors with standard Python exception behavior.

9

10

```python { .api }

11

class EngineIOError(Exception):

12

"""

13

Base exception class for Engine.IO errors.

14

15

All Engine.IO-specific exceptions inherit from this class,

16

allowing for broad exception handling when needed.

17

"""

18

```

19

20

### Content Size Errors

21

22

Exception raised when message content exceeds configured size limitations.

23

24

```python { .api }

25

class ContentTooLongError(EngineIOError):

26

"""

27

Raised when message content exceeds size limits.

28

29

This error occurs when:

30

- Message data is larger than max_http_buffer_size

31

- Packet payload exceeds transport limitations

32

- Binary data exceeds configured thresholds

33

"""

34

```

35

36

### Protocol Errors

37

38

Exception for handling unknown or malformed packet types during communication.

39

40

```python { .api }

41

class UnknownPacketError(EngineIOError):

42

"""

43

Raised when an unknown packet type is encountered.

44

45

This error indicates:

46

- Received packet with invalid type identifier

47

- Protocol version mismatch between client and server

48

- Corrupted packet data during transmission

49

"""

50

```

51

52

### Queue Management Errors

53

54

Exception for empty queue operations in multi-threaded or async environments.

55

56

```python { .api }

57

class QueueEmpty(EngineIOError):

58

"""

59

Raised when attempting to get from an empty queue.

60

61

This error occurs when:

62

- Non-blocking queue operations find no items

63

- Background task queue is exhausted

64

- Message buffer is empty during read operations

65

"""

66

```

67

68

### Socket State Errors

69

70

Exception for operations attempted on closed or invalid socket connections.

71

72

```python { .api }

73

class SocketIsClosedError(EngineIOError):

74

"""

75

Raised when trying to operate on a closed socket.

76

77

This error indicates:

78

- Attempting to send data through closed connection

79

- Socket was disconnected during operation

80

- Connection was terminated by client or server

81

"""

82

```

83

84

### Connection Errors

85

86

Exception for connection establishment and maintenance failures.

87

88

```python { .api }

89

class ConnectionError(EngineIOError):

90

"""

91

Raised when connection fails or is lost.

92

93

This error occurs during:

94

- Initial connection establishment failures

95

- Network connectivity issues

96

- Server unavailability or rejection

97

- Transport-specific connection problems

98

"""

99

```

100

101

## Exception Handling Patterns

102

103

### Basic Exception Handling

104

105

```python

106

import engineio

107

108

eio = engineio.Client()

109

110

try:

111

eio.connect('http://localhost:5000')

112

eio.send('Hello Server!')

113

except engineio.ConnectionError as e:

114

print(f'Connection failed: {e}')

115

except engineio.SocketIsClosedError as e:

116

print(f'Socket closed during operation: {e}')

117

except engineio.EngineIOError as e:

118

print(f'Engine.IO error occurred: {e}')

119

except Exception as e:

120

print(f'Unexpected error: {e}')

121

```

122

123

### Server-Side Exception Handling

124

125

```python

126

import engineio

127

128

eio = engineio.Server()

129

130

@eio.on('message')

131

def on_message(sid, data):

132

try:

133

# Process message data

134

result = process_data(data)

135

eio.send(sid, result)

136

except engineio.ContentTooLongError:

137

eio.send(sid, {'error': 'Message too large'})

138

except engineio.SocketIsClosedError:

139

print(f'Client {sid} disconnected during processing')

140

except engineio.EngineIOError as e:

141

print(f'Engine.IO error for client {sid}: {e}')

142

eio.send(sid, {'error': 'Processing failed'})

143

except Exception as e:

144

print(f'Unexpected error processing message from {sid}: {e}')

145

eio.send(sid, {'error': 'Internal server error'})

146

```

147

148

### Async Exception Handling

149

150

```python

151

import engineio

152

import asyncio

153

154

eio = engineio.AsyncClient()

155

156

@eio.on('connect')

157

async def on_connect():

158

try:

159

await eio.send('Hello Async Server!')

160

except engineio.SocketIsClosedError:

161

print('Connection closed before sending message')

162

except engineio.EngineIOError as e:

163

print(f'Failed to send initial message: {e}')

164

165

async def main():

166

try:

167

await eio.connect('http://localhost:5000')

168

await eio.wait()

169

except engineio.ConnectionError as e:

170

print(f'Connection error: {e}')

171

# Implement retry logic

172

await asyncio.sleep(5)

173

try:

174

await eio.connect('http://localhost:5000')

175

except engineio.ConnectionError:

176

print('Retry failed, giving up')

177

except engineio.EngineIOError as e:

178

print(f'Engine.IO error: {e}')

179

180

asyncio.run(main())

181

```

182

183

### Comprehensive Error Handling

184

185

```python

186

import engineio

187

import logging

188

189

# Configure logging for error tracking

190

logging.basicConfig(level=logging.INFO)

191

logger = logging.getLogger(__name__)

192

193

eio = engineio.Server(logger=True)

194

195

@eio.on('connect')

196

def on_connect(sid, environ):

197

logger.info(f'Client {sid} connected')

198

199

@eio.on('message')

200

def on_message(sid, data):

201

try:

202

# Validate message size

203

if len(str(data)) > 10000: # Custom size check

204

raise engineio.ContentTooLongError('Message exceeds custom limit')

205

206

# Process message

207

response = {'echo': data, 'timestamp': time.time()}

208

eio.send(sid, response)

209

210

except engineio.ContentTooLongError as e:

211

logger.warning(f'Content too long from {sid}: {e}')

212

try:

213

eio.send(sid, {'error': 'Message too large', 'code': 'CONTENT_TOO_LONG'})

214

except engineio.SocketIsClosedError:

215

logger.info(f'Cannot send error response, client {sid} disconnected')

216

217

except engineio.SocketIsClosedError as e:

218

logger.info(f'Client {sid} socket closed: {e}')

219

220

except engineio.UnknownPacketError as e:

221

logger.error(f'Protocol error from {sid}: {e}')

222

try:

223

eio.disconnect(sid)

224

except engineio.EngineIOError:

225

pass # Already disconnected

226

227

except engineio.EngineIOError as e:

228

logger.error(f'Engine.IO error for {sid}: {e}')

229

try:

230

eio.send(sid, {'error': 'Server error', 'code': 'SERVER_ERROR'})

231

except engineio.EngineIOError:

232

logger.error(f'Failed to send error response to {sid}')

233

234

except Exception as e:

235

logger.exception(f'Unexpected error processing message from {sid}')

236

try:

237

eio.send(sid, {'error': 'Internal error', 'code': 'INTERNAL_ERROR'})

238

except engineio.EngineIOError:

239

logger.error(f'Failed to send error response to {sid}')

240

241

@eio.on('disconnect')

242

def on_disconnect(sid):

243

logger.info(f'Client {sid} disconnected')

244

```

245

246

### Context Manager for Error Handling

247

248

```python

249

import engineio

250

from contextlib import contextmanager

251

252

@contextmanager

253

def engineio_error_handler(client_id=None):

254

"""Context manager for standardized Engine.IO error handling"""

255

try:

256

yield

257

except engineio.ConnectionError as e:

258

print(f'Connection error{f" for {client_id}" if client_id else ""}: {e}')

259

raise

260

except engineio.SocketIsClosedError as e:

261

print(f'Socket closed{f" for {client_id}" if client_id else ""}: {e}')

262

except engineio.ContentTooLongError as e:

263

print(f'Content too long{f" for {client_id}" if client_id else ""}: {e}')

264

except engineio.UnknownPacketError as e:

265

print(f'Protocol error{f" for {client_id}" if client_id else ""}: {e}')

266

except engineio.QueueEmpty as e:

267

print(f'Queue empty{f" for {client_id}" if client_id else ""}: {e}')

268

except engineio.EngineIOError as e:

269

print(f'Engine.IO error{f" for {client_id}" if client_id else ""}: {e}')

270

271

# Usage example

272

eio = engineio.Client()

273

274

with engineio_error_handler('client-1'):

275

eio.connect('http://localhost:5000')

276

eio.send('Hello!')

277

```

278

279

### Exception Handling in Event Handlers

280

281

```python

282

import engineio

283

import functools

284

285

def handle_exceptions(handler):

286

"""Decorator for event handler exception handling"""

287

@functools.wraps(handler)

288

def wrapper(*args, **kwargs):

289

try:

290

return handler(*args, **kwargs)

291

except engineio.EngineIOError as e:

292

print(f'Engine.IO error in {handler.__name__}: {e}')

293

except Exception as e:

294

print(f'Unexpected error in {handler.__name__}: {e}')

295

return wrapper

296

297

def handle_async_exceptions(handler):

298

"""Decorator for async event handler exception handling"""

299

@functools.wraps(handler)

300

async def wrapper(*args, **kwargs):

301

try:

302

return await handler(*args, **kwargs)

303

except engineio.EngineIOError as e:

304

print(f'Engine.IO error in {handler.__name__}: {e}')

305

except Exception as e:

306

print(f'Unexpected error in {handler.__name__}: {e}')

307

return wrapper

308

309

eio = engineio.Server()

310

311

@eio.on('connect')

312

@handle_exceptions

313

def on_connect(sid, environ):

314

# Handler code that might raise exceptions

315

risky_operation(sid)

316

317

@eio.on('message')

318

@handle_exceptions

319

def on_message(sid, data):

320

# Message processing that might fail

321

process_message(sid, data)

322

```

323

324

## Error Recovery Strategies

325

326

### Automatic Reconnection

327

328

```python

329

import engineio

330

import time

331

import random

332

333

class ResilientClient:

334

def __init__(self, url, max_retries=5):

335

self.url = url

336

self.max_retries = max_retries

337

self.client = engineio.Client()

338

self.setup_handlers()

339

340

def setup_handlers(self):

341

@self.client.on('disconnect')

342

def on_disconnect():

343

print('Disconnected, attempting to reconnect...')

344

self.reconnect()

345

346

def connect(self):

347

"""Initial connection with retry logic"""

348

for attempt in range(self.max_retries):

349

try:

350

self.client.connect(self.url)

351

print('Connected successfully')

352

return True

353

except engineio.ConnectionError as e:

354

print(f'Connection attempt {attempt + 1} failed: {e}')

355

if attempt < self.max_retries - 1:

356

# Exponential backoff with jitter

357

delay = (2 ** attempt) + random.uniform(0, 1)

358

time.sleep(delay)

359

return False

360

361

def reconnect(self):

362

"""Reconnection logic"""

363

time.sleep(5) # Initial delay

364

if self.connect():

365

print('Reconnected successfully')

366

else:

367

print('Reconnection failed after all attempts')

368

369

def send_safe(self, data):

370

"""Send with error handling"""

371

try:

372

self.client.send(data)

373

return True

374

except engineio.SocketIsClosedError:

375

print('Cannot send, socket is closed')

376

return False

377

except engineio.EngineIOError as e:

378

print(f'Send failed: {e}')

379

return False

380

381

# Usage

382

client = ResilientClient('http://localhost:5000')

383

if client.connect():

384

client.send_safe('Hello!')

385

```

386

387

### Circuit Breaker Pattern

388

389

```python

390

import engineio

391

import time

392

from enum import Enum

393

394

class CircuitState(Enum):

395

CLOSED = "closed"

396

OPEN = "open"

397

HALF_OPEN = "half_open"

398

399

class CircuitBreaker:

400

def __init__(self, failure_threshold=5, recovery_timeout=60):

401

self.failure_threshold = failure_threshold

402

self.recovery_timeout = recovery_timeout

403

self.failure_count = 0

404

self.last_failure_time = None

405

self.state = CircuitState.CLOSED

406

407

def call(self, func, *args, **kwargs):

408

"""Execute function with circuit breaker protection"""

409

if self.state == CircuitState.OPEN:

410

if time.time() - self.last_failure_time > self.recovery_timeout:

411

self.state = CircuitState.HALF_OPEN

412

else:

413

raise engineio.ConnectionError("Circuit breaker is OPEN")

414

415

try:

416

result = func(*args, **kwargs)

417

if self.state == CircuitState.HALF_OPEN:

418

self.state = CircuitState.CLOSED

419

self.failure_count = 0

420

return result

421

except engineio.EngineIOError:

422

self.failure_count += 1

423

self.last_failure_time = time.time()

424

425

if self.failure_count >= self.failure_threshold:

426

self.state = CircuitState.OPEN

427

raise

428

429

# Usage

430

eio = engineio.Client()

431

circuit_breaker = CircuitBreaker()

432

433

def safe_connect():

434

return circuit_breaker.call(eio.connect, 'http://localhost:5000')

435

436

def safe_send(data):

437

return circuit_breaker.call(eio.send, data)

438

```

439

440

## Logging and Monitoring

441

442

### Enhanced Error Logging

443

444

```python

445

import engineio

446

import logging

447

import traceback

448

from datetime import datetime

449

450

class EngineIOErrorLogger:

451

def __init__(self, logger_name='engineio_errors'):

452

self.logger = logging.getLogger(logger_name)

453

self.setup_logging()

454

455

def setup_logging(self):

456

handler = logging.StreamHandler()

457

formatter = logging.Formatter(

458

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

459

)

460

handler.setFormatter(formatter)

461

self.logger.addHandler(handler)

462

self.logger.setLevel(logging.INFO)

463

464

def log_exception(self, exception, context=None):

465

"""Log Engine.IO exception with context"""

466

error_type = type(exception).__name__

467

timestamp = datetime.now().isoformat()

468

469

log_data = {

470

'timestamp': timestamp,

471

'error_type': error_type,

472

'error_message': str(exception),

473

'context': context or {}

474

}

475

476

if isinstance(exception, engineio.ConnectionError):

477

self.logger.error(f'Connection Error: {log_data}')

478

elif isinstance(exception, engineio.SocketIsClosedError):

479

self.logger.warning(f'Socket Closed: {log_data}')

480

elif isinstance(exception, engineio.ContentTooLongError):

481

self.logger.warning(f'Content Too Long: {log_data}')

482

elif isinstance(exception, engineio.EngineIOError):

483

self.logger.error(f'Engine.IO Error: {log_data}')

484

self.logger.debug(traceback.format_exc())

485

else:

486

self.logger.error(f'Unexpected Error: {log_data}')

487

self.logger.debug(traceback.format_exc())

488

489

# Usage

490

error_logger = EngineIOErrorLogger()

491

eio = engineio.Server()

492

493

@eio.on('message')

494

def on_message(sid, data):

495

try:

496

# Process message

497

pass

498

except engineio.EngineIOError as e:

499

error_logger.log_exception(e, {'sid': sid, 'data_type': type(data).__name__})

500

```