or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

auth-permissions.mdcontent-negotiation.mddecorators.mdfields-validation.mdgeneric-views.mdindex.mdpagination-filtering.mdrequest-response.mdrouters-urls.mdserializers.mdstatus-exceptions.mdtesting.mdviews-viewsets.md

status-exceptions.mddocs/

0

# Status Codes and Exceptions

1

2

Complete HTTP status code constants and structured exception classes for consistent error handling in Django REST Framework.

3

4

## Capabilities

5

6

### HTTP Status Constants

7

8

Complete set of HTTP status code constants for consistent API responses.

9

10

```python { .api }

11

# Informational responses (1xx)

12

HTTP_100_CONTINUE = 100

13

HTTP_101_SWITCHING_PROTOCOLS = 101

14

15

# Successful responses (2xx)

16

HTTP_200_OK = 200

17

HTTP_201_CREATED = 201

18

HTTP_202_ACCEPTED = 202

19

HTTP_203_NON_AUTHORITATIVE_INFORMATION = 203

20

HTTP_204_NO_CONTENT = 204

21

HTTP_205_RESET_CONTENT = 205

22

HTTP_206_PARTIAL_CONTENT = 206

23

HTTP_207_MULTI_STATUS = 207

24

HTTP_208_ALREADY_REPORTED = 208

25

HTTP_226_IM_USED = 226

26

27

# Redirection messages (3xx)

28

HTTP_300_MULTIPLE_CHOICES = 300

29

HTTP_301_MOVED_PERMANENTLY = 301

30

HTTP_302_FOUND = 302

31

HTTP_303_SEE_OTHER = 303

32

HTTP_304_NOT_MODIFIED = 304

33

HTTP_305_USE_PROXY = 305

34

HTTP_306_RESERVED = 306

35

HTTP_307_TEMPORARY_REDIRECT = 307

36

HTTP_308_PERMANENT_REDIRECT = 308

37

38

# Client error responses (4xx)

39

HTTP_400_BAD_REQUEST = 400

40

HTTP_401_UNAUTHORIZED = 401

41

HTTP_402_PAYMENT_REQUIRED = 402

42

HTTP_403_FORBIDDEN = 403

43

HTTP_404_NOT_FOUND = 404

44

HTTP_405_METHOD_NOT_ALLOWED = 405

45

HTTP_406_NOT_ACCEPTABLE = 406

46

HTTP_407_PROXY_AUTHENTICATION_REQUIRED = 407

47

HTTP_408_REQUEST_TIMEOUT = 408

48

HTTP_409_CONFLICT = 409

49

HTTP_410_GONE = 410

50

HTTP_411_LENGTH_REQUIRED = 411

51

HTTP_412_PRECONDITION_FAILED = 412

52

HTTP_413_REQUEST_ENTITY_TOO_LARGE = 413

53

HTTP_414_REQUEST_URI_TOO_LONG = 414

54

HTTP_415_UNSUPPORTED_MEDIA_TYPE = 415

55

HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE = 416

56

HTTP_417_EXPECTATION_FAILED = 417

57

HTTP_418_IM_A_TEAPOT = 418

58

HTTP_422_UNPROCESSABLE_ENTITY = 422

59

HTTP_423_LOCKED = 423

60

HTTP_424_FAILED_DEPENDENCY = 424

61

HTTP_426_UPGRADE_REQUIRED = 426

62

HTTP_428_PRECONDITION_REQUIRED = 428

63

HTTP_429_TOO_MANY_REQUESTS = 429

64

HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE = 431

65

HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS = 451

66

67

# Server error responses (5xx)

68

HTTP_500_INTERNAL_SERVER_ERROR = 500

69

HTTP_501_NOT_IMPLEMENTED = 501

70

HTTP_502_BAD_GATEWAY = 502

71

HTTP_503_SERVICE_UNAVAILABLE = 503

72

HTTP_504_GATEWAY_TIMEOUT = 504

73

HTTP_505_HTTP_VERSION_NOT_SUPPORTED = 505

74

HTTP_506_VARIANT_ALSO_NEGOTIATES = 506

75

HTTP_507_INSUFFICIENT_STORAGE = 507

76

HTTP_508_LOOP_DETECTED = 508

77

HTTP_509_BANDWIDTH_LIMIT_EXCEEDED = 509

78

HTTP_510_NOT_EXTENDED = 510

79

HTTP_511_NETWORK_AUTHENTICATION_REQUIRED = 511

80

```

81

82

### Status Code Utility Functions

83

84

Functions for categorizing HTTP status codes.

85

86

```python { .api }

87

def is_informational(code):

88

"""

89

Check if HTTP status code is informational (1xx).

90

91

Args:

92

code (int): HTTP status code

93

94

Returns:

95

bool: True if code is 1xx

96

"""

97

98

def is_success(code):

99

"""

100

Check if HTTP status code indicates success (2xx).

101

102

Args:

103

code (int): HTTP status code

104

105

Returns:

106

bool: True if code is 2xx

107

"""

108

109

def is_redirect(code):

110

"""

111

Check if HTTP status code indicates redirection (3xx).

112

113

Args:

114

code (int): HTTP status code

115

116

Returns:

117

bool: True if code is 3xx

118

"""

119

120

def is_client_error(code):

121

"""

122

Check if HTTP status code indicates client error (4xx).

123

124

Args:

125

code (int): HTTP status code

126

127

Returns:

128

bool: True if code is 4xx

129

"""

130

131

def is_server_error(code):

132

"""

133

Check if HTTP status code indicates server error (5xx).

134

135

Args:

136

code (int): HTTP status code

137

138

Returns:

139

bool: True if code is 5xx

140

"""

141

```

142

143

### Exception Classes

144

145

Structured exception classes for API error handling.

146

147

```python { .api }

148

class ErrorDetail(str):

149

"""

150

String subclass that stores an error code in addition to the error message.

151

"""

152

def __new__(cls, string, code=None):

153

"""

154

Create new ErrorDetail instance.

155

156

Args:

157

string (str): Error message

158

code (str): Error code identifier

159

"""

160

self = super().__new__(cls, string)

161

self.code = code

162

return self

163

164

def __eq__(self, other):

165

"""Compare ErrorDetail instances."""

166

167

def __ne__(self, other):

168

"""Compare ErrorDetail instances."""

169

170

def __repr__(self):

171

"""String representation of ErrorDetail."""

172

173

def __hash__(self):

174

"""Hash for ErrorDetail."""

175

176

class APIException(Exception):

177

"""

178

Base class for all API exceptions.

179

"""

180

status_code = status.HTTP_500_INTERNAL_SERVER_ERROR

181

default_detail = 'A server error occurred.'

182

default_code = 'error'

183

184

def __init__(self, detail=None, code=None):

185

"""

186

Initialize API exception.

187

188

Args:

189

detail (str or dict): Error detail message or dictionary

190

code (str): Error code identifier

191

"""

192

if detail is None:

193

detail = self.default_detail

194

if code is None:

195

code = self.default_code

196

197

self.detail = _get_error_details(detail, code)

198

199

def __str__(self):

200

"""String representation of exception."""

201

return str(self.detail)

202

203

def get_codes(self):

204

"""

205

Get error codes from exception detail.

206

207

Returns:

208

Error codes in same structure as detail

209

"""

210

return _get_codes(self.detail)

211

212

def get_full_details(self):

213

"""

214

Get full error details including codes.

215

216

Returns:

217

Full error details with messages and codes

218

"""

219

return _get_full_details(self.detail)

220

221

class ValidationError(APIException):

222

"""

223

Exception for validation errors (400 Bad Request).

224

"""

225

status_code = status.HTTP_400_BAD_REQUEST

226

default_detail = 'Invalid input.'

227

default_code = 'invalid'

228

229

class ParseError(APIException):

230

"""

231

Exception for malformed request data (400 Bad Request).

232

"""

233

status_code = status.HTTP_400_BAD_REQUEST

234

default_detail = 'Malformed request.'

235

default_code = 'parse_error'

236

237

class AuthenticationFailed(APIException):

238

"""

239

Exception for authentication failures (401 Unauthorized).

240

"""

241

status_code = status.HTTP_401_UNAUTHORIZED

242

default_detail = 'Incorrect authentication credentials.'

243

default_code = 'authentication_failed'

244

245

class NotAuthenticated(APIException):

246

"""

247

Exception for missing authentication (401 Unauthorized).

248

"""

249

status_code = status.HTTP_401_UNAUTHORIZED

250

default_detail = 'Authentication credentials were not provided.'

251

default_code = 'not_authenticated'

252

253

class PermissionDenied(APIException):

254

"""

255

Exception for permission errors (403 Forbidden).

256

"""

257

status_code = status.HTTP_403_FORBIDDEN

258

default_detail = 'You do not have permission to perform this action.'

259

default_code = 'permission_denied'

260

261

class NotFound(APIException):

262

"""

263

Exception for resource not found (404 Not Found).

264

"""

265

status_code = status.HTTP_404_NOT_FOUND

266

default_detail = 'Not found.'

267

default_code = 'not_found'

268

269

class MethodNotAllowed(APIException):

270

"""

271

Exception for unsupported HTTP methods (405 Method Not Allowed).

272

"""

273

status_code = status.HTTP_405_METHOD_NOT_ALLOWED

274

default_detail = 'Method not allowed.'

275

default_code = 'method_not_allowed'

276

277

def __init__(self, method, detail=None, code=None):

278

"""

279

Args:

280

method (str): HTTP method that was not allowed

281

detail: Custom error detail

282

code: Custom error code

283

"""

284

if detail is None:

285

detail = f'Method "{method}" not allowed.'

286

super().__init__(detail, code)

287

288

class NotAcceptable(APIException):

289

"""

290

Exception for unsupported media types (406 Not Acceptable).

291

"""

292

status_code = status.HTTP_406_NOT_ACCEPTABLE

293

default_detail = 'Could not satisfy the request Accept header.'

294

default_code = 'not_acceptable'

295

296

def __init__(self, detail=None, code=None, available_renderers=None):

297

"""

298

Args:

299

detail: Custom error detail

300

code: Custom error code

301

available_renderers (list): List of available renderer media types

302

"""

303

self.available_renderers = available_renderers

304

super().__init__(detail, code)

305

306

class UnsupportedMediaType(APIException):

307

"""

308

Exception for unsupported request media types (415 Unsupported Media Type).

309

"""

310

status_code = status.HTTP_415_UNSUPPORTED_MEDIA_TYPE

311

default_detail = 'Unsupported media type in request.'

312

default_code = 'unsupported_media_type'

313

314

def __init__(self, media_type, detail=None, code=None):

315

"""

316

Args:

317

media_type (str): Unsupported media type

318

detail: Custom error detail

319

code: Custom error code

320

"""

321

if detail is None:

322

detail = f'Unsupported media type "{media_type}" in request.'

323

super().__init__(detail, code)

324

325

class Throttled(APIException):

326

"""

327

Exception for rate limit exceeded (429 Too Many Requests).

328

"""

329

status_code = status.HTTP_429_TOO_MANY_REQUESTS

330

default_detail = 'Request was throttled.'

331

default_code = 'throttled'

332

extra_detail_singular = 'Expected available in {wait} second.'

333

extra_detail_plural = 'Expected available in {wait} seconds.'

334

335

def __init__(self, wait=None, detail=None, code=None):

336

"""

337

Args:

338

wait (int): Seconds to wait before next request

339

detail: Custom error detail

340

code: Custom error code

341

"""

342

if detail is None:

343

if wait is not None:

344

if wait == 1:

345

detail = self.extra_detail_singular.format(wait=wait)

346

else:

347

detail = self.extra_detail_plural.format(wait=wait)

348

else:

349

detail = self.default_detail

350

351

self.wait = wait

352

super().__init__(detail, code)

353

```

354

355

### Error Handler Functions

356

357

Functions for handling different types of errors.

358

359

```python { .api }

360

def server_error(request, *args, **kwargs):

361

"""

362

Generic 500 error handler for API views.

363

364

Args:

365

request: HTTP request object

366

*args: Additional arguments

367

**kwargs: Additional keyword arguments

368

369

Returns:

370

Response: 500 error response

371

"""

372

373

def bad_request(request, exception, *args, **kwargs):

374

"""

375

Generic 400 error handler for API views.

376

377

Args:

378

request: HTTP request object

379

exception: Exception that caused the error

380

*args: Additional arguments

381

**kwargs: Additional keyword arguments

382

383

Returns:

384

Response: 400 error response

385

"""

386

```

387

388

## Usage Examples

389

390

### Using Status Constants

391

392

```python

393

from rest_framework import status

394

from rest_framework.response import Response

395

from rest_framework.views import APIView

396

397

class BookView(APIView):

398

def get(self, request):

399

books = Book.objects.all()

400

serializer = BookSerializer(books, many=True)

401

return Response(serializer.data, status=status.HTTP_200_OK)

402

403

def post(self, request):

404

serializer = BookSerializer(data=request.data)

405

if serializer.is_valid():

406

serializer.save()

407

return Response(serializer.data, status=status.HTTP_201_CREATED)

408

return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

409

410

def delete(self, request, pk):

411

try:

412

book = Book.objects.get(pk=pk)

413

book.delete()

414

return Response(status=status.HTTP_204_NO_CONTENT)

415

except Book.DoesNotExist:

416

return Response(

417

{'error': 'Book not found'},

418

status=status.HTTP_404_NOT_FOUND

419

)

420

```

421

422

### Raising Custom Exceptions

423

424

```python

425

from rest_framework import exceptions

426

from rest_framework.views import APIView

427

428

class BookView(APIView):

429

def get_object(self, pk):

430

try:

431

return Book.objects.get(pk=pk)

432

except Book.DoesNotExist:

433

raise exceptions.NotFound('Book not found')

434

435

def post(self, request):

436

# Validate permissions

437

if not request.user.has_perm('myapp.add_book'):

438

raise exceptions.PermissionDenied(

439

'You do not have permission to create books'

440

)

441

442

# Validate data

443

if not request.data.get('title'):

444

raise exceptions.ValidationError({

445

'title': ['This field is required.']

446

})

447

448

# Create book

449

serializer = BookSerializer(data=request.data)

450

if serializer.is_valid():

451

serializer.save()

452

return Response(serializer.data, status=status.HTTP_201_CREATED)

453

454

# Raise validation error with serializer errors

455

raise exceptions.ValidationError(serializer.errors)

456

```

457

458

### Custom Exception Classes

459

460

```python

461

from rest_framework import exceptions, status

462

463

class BookNotAvailable(exceptions.APIException):

464

status_code = status.HTTP_409_CONFLICT

465

default_detail = 'Book is not available for checkout'

466

default_code = 'book_not_available'

467

468

class LibraryCardExpired(exceptions.APIException):

469

status_code = status.HTTP_403_FORBIDDEN

470

default_detail = 'Library card has expired'

471

default_code = 'card_expired'

472

473

def __init__(self, expiry_date=None):

474

if expiry_date:

475

detail = f'Library card expired on {expiry_date}'

476

else:

477

detail = self.default_detail

478

super().__init__(detail)

479

480

# Usage in views

481

class CheckoutView(APIView):

482

def post(self, request):

483

book_id = request.data.get('book_id')

484

book = Book.objects.get(pk=book_id)

485

486

if not book.is_available:

487

raise BookNotAvailable()

488

489

if request.user.library_card.is_expired:

490

raise LibraryCardExpired(request.user.library_card.expiry_date)

491

492

# Process checkout

493

return Response({'status': 'checked out'})

494

```

495

496

### Exception Detail Structures

497

498

```python

499

from rest_framework import exceptions

500

501

# Simple string detail

502

raise exceptions.ValidationError('Invalid data')

503

# Response: {"detail": "Invalid data"}

504

505

# Dictionary detail for field errors

506

raise exceptions.ValidationError({

507

'title': ['This field is required.'],

508

'isbn': ['ISBN must be 13 digits.']

509

})

510

# Response: {

511

# "title": ["This field is required."],

512

# "isbn": ["ISBN must be 13 digits."]

513

# }

514

515

# List detail for multiple errors

516

raise exceptions.ValidationError([

517

'First error message',

518

'Second error message'

519

])

520

# Response: [

521

# "First error message",

522

# "Second error message"

523

# ]

524

525

# Error details with codes

526

from rest_framework.exceptions import ErrorDetail

527

528

raise exceptions.ValidationError({

529

'title': [ErrorDetail('This field is required.', code='required')],

530

'pages': [ErrorDetail('Must be a positive integer.', code='invalid')]

531

})

532

```

533

534

### Status Code Utilities

535

536

```python

537

from rest_framework import status

538

539

def handle_response(response_code):

540

if status.is_success(response_code):

541

print("Operation successful")

542

elif status.is_client_error(response_code):

543

print("Client error occurred")

544

elif status.is_server_error(response_code):

545

print("Server error occurred")

546

elif status.is_redirect(response_code):

547

print("Redirect required")

548

elif status.is_informational(response_code):

549

print("Informational response")

550

551

# Example usage

552

handle_response(status.HTTP_201_CREATED) # "Operation successful"

553

handle_response(status.HTTP_404_NOT_FOUND) # "Client error occurred"

554

handle_response(status.HTTP_500_INTERNAL_SERVER_ERROR) # "Server error occurred"

555

```

556

557

## Utility Functions

558

559

```python { .api }

560

def _get_error_details(data, default_code=None):

561

"""

562

Process error data into ErrorDetail instances.

563

564

Args:

565

data: Error data (string, dict, or list)

566

default_code (str): Default error code

567

568

Returns:

569

Processed error details

570

"""

571

572

def _get_codes(detail):

573

"""

574

Extract error codes from error detail structure.

575

576

Args:

577

detail: Error detail structure

578

579

Returns:

580

Error codes in same structure

581

"""

582

583

def _get_full_details(detail):

584

"""

585

Get full error details including both messages and codes.

586

587

Args:

588

detail: Error detail structure

589

590

Returns:

591

Full error details with messages and codes

592

"""

593

```