or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-features.mdasync-requests.mdexceptions.mdindex.mdmodels.mdsessions.mdsync-requests.md

exceptions.mddocs/

0

# Exception Handling

1

2

Comprehensive exception hierarchy for handling various error conditions during HTTP operations. These exceptions provide detailed error information and enable robust error handling patterns. All niquests exceptions inherit from `RequestException`, which inherits from Python's built-in `IOError`.

3

4

## Exception Hierarchy

5

6

```

7

IOError

8

└── RequestException

9

├── HTTPError

10

├── ConnectionError

11

│ ├── ProxyError

12

│ ├── SSLError

13

│ └── ConnectTimeout (also inherits from Timeout)

14

├── Timeout

15

│ └── ReadTimeout

16

├── URLRequired

17

├── TooManyRedirects

18

├── InvalidJSONError

19

│ └── JSONDecodeError

20

├── InvalidURL

21

├── InvalidSchema

22

├── InvalidHeader

23

├── MissingSchema

24

├── ChunkedEncodingError

25

├── ContentDecodingError

26

├── StreamConsumedError

27

├── RetryError

28

├── UnrewindableBodyError

29

└── MultiplexingError

30

```

31

32

## Capabilities

33

34

### Base Exception Classes

35

36

Core exception classes that form the foundation of niquests error handling.

37

38

```python { .api }

39

class RequestException(IOError):

40

"""

41

Base exception class for all requests-related exceptions.

42

43

There was an ambiguous exception that occurred while handling your request.

44

All other niquests exceptions inherit from this class.

45

"""

46

47

def __init__(self, *args, **kwargs):

48

"""

49

Initialize RequestException with request and response objects.

50

51

Args:

52

*args: Exception arguments

53

**kwargs: May include 'request' and 'response' objects

54

"""

55

56

# Instance attributes

57

request: PreparedRequest | None # The request that caused the exception

58

response: Response | None # The response (if any) that caused the exception

59

60

class HTTPError(RequestException):

61

"""

62

An HTTP error occurred.

63

64

Raised when an HTTP request returns an unsuccessful status code

65

(4xx or 5xx) and raise_for_status() is called.

66

"""

67

68

class ConnectionError(RequestException):

69

"""

70

A Connection error occurred.

71

72

Raised when there's a network-level connection problem,

73

such as a refused connection or unreachable host.

74

"""

75

```

76

77

### Timeout Exceptions

78

79

Exceptions related to request timeouts and timing issues.

80

81

```python { .api }

82

class Timeout(RequestException):

83

"""

84

The request timed out.

85

86

Catching this error will catch both ConnectTimeout and ReadTimeout.

87

This is the base class for all timeout-related exceptions.

88

"""

89

90

class ConnectTimeout(ConnectionError, Timeout):

91

"""

92

The request timed out while trying to connect to the remote server.

93

94

Requests that produced this error are safe to retry as the connection

95

was never established.

96

"""

97

98

class ReadTimeout(Timeout):

99

"""

100

The server did not send any data in the allotted amount of time.

101

102

The connection was established but the server failed to send data

103

within the specified timeout period.

104

"""

105

```

106

107

### SSL and Security Exceptions

108

109

Exceptions related to SSL/TLS and security issues.

110

111

```python { .api }

112

class SSLError(ConnectionError):

113

"""

114

An SSL error occurred.

115

116

Raised when there's an SSL/TLS-related problem such as:

117

- Certificate verification failures

118

- SSL handshake failures

119

- Protocol version mismatches

120

"""

121

122

class ProxyError(ConnectionError):

123

"""

124

A proxy error occurred.

125

126

Raised when there's an issue with proxy configuration or

127

the proxy server itself.

128

"""

129

```

130

131

### URL and Schema Exceptions

132

133

Exceptions related to URL formatting and validation.

134

135

```python { .api }

136

class URLRequired(RequestException):

137

"""

138

A valid URL is required to make a request.

139

140

Raised when a request is attempted without providing a URL.

141

"""

142

143

class InvalidURL(RequestException, ValueError):

144

"""

145

The URL provided was somehow invalid.

146

147

Raised when the URL cannot be parsed or contains invalid characters.

148

"""

149

150

class InvalidSchema(RequestException, ValueError):

151

"""

152

The URL scheme provided is either invalid or unsupported.

153

154

Raised for unsupported schemes like 'ftp://' or malformed schemes.

155

"""

156

157

class MissingSchema(RequestException, ValueError):

158

"""

159

The URL scheme (e.g. http or https) is missing.

160

161

Raised when a URL is provided without a protocol scheme.

162

"""

163

164

class InvalidHeader(RequestException, ValueError):

165

"""

166

The header value provided was somehow invalid.

167

168

Raised when HTTP headers contain invalid characters or formatting.

169

"""

170

```

171

172

### Content and Encoding Exceptions

173

174

Exceptions related to response content processing and encoding.

175

176

```python { .api }

177

class InvalidJSONError(RequestException):

178

"""

179

A JSON error occurred.

180

181

Base class for JSON-related errors during response processing.

182

"""

183

184

class JSONDecodeError(InvalidJSONError, json.JSONDecodeError):

185

"""

186

Couldn't decode the text into json.

187

188

Raised when response.json() is called but the response content

189

is not valid JSON.

190

"""

191

192

class ChunkedEncodingError(RequestException):

193

"""

194

The server declared chunked encoding but sent an invalid chunk.

195

196

Raised when there's a problem with chunked transfer encoding

197

in the response.

198

"""

199

200

class ContentDecodingError(RequestException):

201

"""

202

Failed to decode response content.

203

204

Raised when automatic content decompression (gzip, deflate, etc.)

205

fails due to corrupted or invalid compressed data.

206

"""

207

208

class StreamConsumedError(RequestException, TypeError):

209

"""

210

The content for this response was already consumed.

211

212

Raised when attempting to read response content that has already

213

been consumed by a previous operation.

214

"""

215

```

216

217

### Advanced Operation Exceptions

218

219

Exceptions for advanced features and operations.

220

221

```python { .api }

222

class TooManyRedirects(RequestException):

223

"""

224

Too many redirects.

225

226

Raised when the maximum number of redirects is exceeded.

227

"""

228

229

class RetryError(RequestException):

230

"""

231

Custom retries logic failed.

232

233

Raised when retry mechanisms are exhausted or encounter

234

unrecoverable errors.

235

"""

236

237

class UnrewindableBodyError(RequestException):

238

"""

239

Requests encountered an error when trying to rewind a body.

240

241

Raised when attempting to retry a request with a body that

242

cannot be rewound (e.g., a consumed stream).

243

"""

244

245

class MultiplexingError(RequestException):

246

"""

247

Requests encountered an unresolvable error in multiplexed mode.

248

249

Raised when HTTP/2 or HTTP/3 multiplexing encounters errors

250

that cannot be resolved.

251

"""

252

```

253

254

### Warning Classes

255

256

Warning classes for non-fatal issues and deprecation notices.

257

258

```python { .api }

259

class RequestsWarning(Warning):

260

"""

261

Base warning for Requests.

262

263

Base class for all niquests-related warnings.

264

"""

265

266

class FileModeWarning(RequestsWarning, DeprecationWarning):

267

"""

268

A file was opened in text mode, but Requests determined its binary length.

269

270

Warning raised when files are opened in text mode but should be

271

opened in binary mode for proper handling.

272

"""

273

274

class RequestsDependencyWarning(RequestsWarning):

275

"""

276

An imported dependency doesn't match the expected version range.

277

278

Warning raised when dependency versions may cause compatibility issues.

279

"""

280

```

281

282

## Usage Examples

283

284

### Basic Exception Handling

285

286

```python

287

import niquests

288

289

try:

290

response = niquests.get('https://api.example.com/data', timeout=5.0)

291

response.raise_for_status() # Raise HTTPError for bad status codes

292

data = response.json()

293

print("Success:", data)

294

295

except niquests.HTTPError as e:

296

print(f"HTTP Error {e.response.status_code}: {e.response.reason}")

297

# Access the response even when there's an error

298

if e.response.headers.get('content-type', '').startswith('application/json'):

299

error_details = e.response.json()

300

print(f"Error details: {error_details}")

301

302

except niquests.ConnectionError:

303

print("Failed to connect to the server")

304

305

except niquests.Timeout:

306

print("Request timed out")

307

308

except niquests.RequestException as e:

309

print(f"An error occurred: {e}")

310

```

311

312

### Specific Exception Handling

313

314

```python

315

import niquests

316

317

def robust_api_call(url, max_retries=3):

318

"""Make API call with comprehensive error handling."""

319

320

for attempt in range(max_retries):

321

try:

322

response = niquests.get(url, timeout=(5.0, 30.0))

323

response.raise_for_status()

324

return response.json()

325

326

except niquests.ConnectTimeout:

327

print(f"Connection timeout on attempt {attempt + 1}")

328

if attempt == max_retries - 1:

329

raise

330

331

except niquests.ReadTimeout:

332

print(f"Read timeout on attempt {attempt + 1}")

333

if attempt == max_retries - 1:

334

raise

335

336

except niquests.SSLError as e:

337

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

338

# SSL errors are usually not retryable

339

raise

340

341

except niquests.HTTPError as e:

342

status_code = e.response.status_code

343

if status_code >= 500:

344

# Server errors might be retryable

345

print(f"Server error {status_code} on attempt {attempt + 1}")

346

if attempt == max_retries - 1:

347

raise

348

else:

349

# Client errors (4xx) are usually not retryable

350

print(f"Client error {status_code}: {e.response.reason}")

351

raise

352

353

except niquests.JSONDecodeError:

354

print("Response is not valid JSON")

355

print(f"Response content: {response.text[:200]}...")

356

raise

357

358

except niquests.TooManyRedirects:

359

print("Too many redirects - possible redirect loop")

360

raise

361

362

# Wait before retry

363

if attempt < max_retries - 1:

364

time.sleep(2 ** attempt) # Exponential backoff

365

366

raise niquests.RequestException("Max retries exceeded")

367

368

# Usage

369

try:

370

data = robust_api_call('https://api.example.com/data')

371

print("API call successful:", data)

372

except niquests.RequestException as e:

373

print(f"API call failed: {e}")

374

```

375

376

### URL Validation and Error Handling

377

378

```python

379

import niquests

380

381

def validate_and_fetch(url):

382

"""Validate URL and fetch content with proper error handling."""

383

384

try:

385

# This will raise various URL-related exceptions

386

response = niquests.get(url)

387

return response.text

388

389

except niquests.MissingSchema:

390

print("URL is missing a scheme (http:// or https://)")

391

# Try to fix by adding https://

392

return validate_and_fetch(f"https://{url}")

393

394

except niquests.InvalidSchema as e:

395

print(f"Invalid or unsupported URL scheme: {e}")

396

raise

397

398

except niquests.InvalidURL as e:

399

print(f"Malformed URL: {e}")

400

raise

401

402

except niquests.ConnectionError:

403

print("Could not connect to the server")

404

raise

405

406

# Usage examples

407

try:

408

content = validate_and_fetch("example.com") # Missing scheme

409

print("Content fetched successfully")

410

except niquests.RequestException as e:

411

print(f"Failed to fetch content: {e}")

412

```

413

414

### Async Exception Handling

415

416

```python

417

import asyncio

418

import niquests

419

420

async def async_request_with_error_handling(url):

421

"""Async request with comprehensive error handling."""

422

423

try:

424

response = await niquests.aget(url, timeout=10.0)

425

426

async with response: # Ensure response is closed

427

response.raise_for_status()

428

data = await response.json()

429

return data

430

431

except niquests.HTTPError as e:

432

print(f"HTTP Error: {e.response.status_code}")

433

# Can still access response data

434

error_content = await e.response.text

435

print(f"Error response: {error_content}")

436

raise

437

438

except niquests.ConnectionError:

439

print("Connection failed")

440

raise

441

442

except niquests.Timeout:

443

print("Request timed out")

444

raise

445

446

except niquests.JSONDecodeError:

447

print("Invalid JSON response")

448

raise

449

450

# Usage

451

async def main():

452

try:

453

data = await async_request_with_error_handling('https://api.example.com/data')

454

print("Success:", data)

455

except niquests.RequestException as e:

456

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

457

458

asyncio.run(main())

459

```

460

461

### Custom Exception Handling

462

463

```python

464

import niquests

465

import logging

466

467

# Set up logging

468

logging.basicConfig(level=logging.INFO)

469

logger = logging.getLogger(__name__)

470

471

class APIClient:

472

"""Example API client with comprehensive error handling."""

473

474

def __init__(self, base_url, timeout=30.0):

475

self.base_url = base_url

476

self.timeout = timeout

477

self.session = niquests.Session()

478

479

def _handle_request_error(self, e, url):

480

"""Centralized error handling and logging."""

481

482

if isinstance(e, niquests.HTTPError):

483

status = e.response.status_code

484

logger.error(f"HTTP {status} error for {url}: {e.response.reason}")

485

486

# Log response content for debugging

487

try:

488

error_data = e.response.json()

489

logger.error(f"Error response: {error_data}")

490

except niquests.JSONDecodeError:

491

logger.error(f"Error response (non-JSON): {e.response.text[:500]}")

492

493

elif isinstance(e, niquests.ConnectionError):

494

logger.error(f"Connection error for {url}: {e}")

495

496

elif isinstance(e, niquests.Timeout):

497

logger.error(f"Timeout error for {url}: {e}")

498

499

elif isinstance(e, niquests.JSONDecodeError):

500

logger.error(f"JSON decode error for {url}: {e}")

501

502

else:

503

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

504

505

def get(self, endpoint, **kwargs):

506

"""Make GET request with error handling."""

507

url = f"{self.base_url}/{endpoint.lstrip('/')}"

508

509

try:

510

response = self.session.get(url, timeout=self.timeout, **kwargs)

511

response.raise_for_status()

512

return response.json()

513

514

except niquests.RequestException as e:

515

self._handle_request_error(e, url)

516

raise

517

518

def __enter__(self):

519

return self

520

521

def __exit__(self, exc_type, exc_val, exc_tb):

522

self.session.close()

523

524

# Usage

525

with APIClient('https://api.example.com') as client:

526

try:

527

users = client.get('/users')

528

print(f"Found {len(users)} users")

529

except niquests.RequestException:

530

print("Failed to fetch users")

531

```

532

533

## Best Practices

534

535

### Exception Handling Strategy

536

537

1. **Catch specific exceptions** rather than using broad `except` clauses

538

2. **Handle retryable vs non-retryable errors** differently

539

3. **Log errors appropriately** with relevant context

540

4. **Preserve error information** for debugging

541

5. **Use response data even for errors** when available

542

543

### Common Patterns

544

545

```python

546

# Pattern 1: Catch and retry

547

def retry_request(url, max_retries=3):

548

for i in range(max_retries):

549

try:

550

return niquests.get(url, timeout=10.0)

551

except (niquests.ConnectionError, niquests.Timeout):

552

if i == max_retries - 1:

553

raise

554

time.sleep(2 ** i)

555

556

# Pattern 2: Graceful degradation

557

def get_user_data(user_id, fallback=None):

558

try:

559

response = niquests.get(f'/api/users/{user_id}')

560

response.raise_for_status()

561

return response.json()

562

except niquests.RequestException:

563

logger.warning(f"Failed to fetch user {user_id}, using fallback")

564

return fallback or {'id': user_id, 'name': 'Unknown User'}

565

566

# Pattern 3: Error categorization

567

def categorize_error(exception):

568

if isinstance(exception, niquests.HTTPError):

569

status = exception.response.status_code

570

if 400 <= status < 500:

571

return 'client_error'

572

elif 500 <= status < 600:

573

return 'server_error'

574

elif isinstance(exception, niquests.ConnectionError):

575

return 'network_error'

576

elif isinstance(exception, niquests.Timeout):

577

return 'timeout_error'

578

return 'unknown_error'

579

```