or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication.mdexceptions.mdhttp-operations.mdindex.mdnavigation.mdtemplated-links.mdutilities.md

exceptions.mddocs/

0

# Exception Handling

1

2

Comprehensive exception hierarchy for handling HTTP errors, parsing failures, and navigation issues. RestNavigator provides detailed error information to help diagnose and handle API interaction problems.

3

4

## Capabilities

5

6

### Main Exception Class

7

8

Primary exception raised for HTTP error responses with detailed context information.

9

10

```python { .api }

11

class HALNavigatorError(Exception):

12

def __init__(self, message: str, nav: 'HALNavigator', response):

13

"""

14

Exception raised for HTTP error responses (4xx, 5xx).

15

16

Parameters:

17

- message: Human-readable error message

18

- nav: HALNavigator instance that caused the error

19

- response: HTTP response object from requests

20

"""

21

22

@property

23

def nav(self) -> 'HALNavigator':

24

"""Navigator that caused the error"""

25

26

@property

27

def response(self):

28

"""HTTP response object"""

29

30

@property

31

def message(self) -> str:

32

"""Error message"""

33

34

@property

35

def status(self) -> int:

36

"""HTTP status code"""

37

```

38

39

### URL and Scheme Validation Errors

40

41

Exceptions for invalid URLs and protocol issues.

42

43

```python { .api }

44

class WileECoyoteException(ValueError):

45

"""

46

Raised for bad URL schemes.

47

48

Occurs when URL has invalid or missing scheme.

49

"""

50

51

class ZachMorrisException(ValueError):

52

"""

53

Raised for URLs with too many schemes.

54

55

Occurs when URL has multiple conflicting schemes.

56

"""

57

```

58

59

### Resource State Errors

60

61

Exceptions for accessing uninitialized or unavailable resource data.

62

63

```python { .api }

64

class NoResponseError(ValueError):

65

"""

66

Raised when accessing unfetched navigator fields.

67

68

Occurs when trying to access state/response data

69

before navigator has been resolved.

70

"""

71

```

72

73

### JSON Parsing Errors

74

75

Exception for non-JSON response handling.

76

77

```python { .api }

78

class UnexpectedlyNotJSON(TypeError):

79

def __init__(self, uri: str, response):

80

"""

81

Raised for non-JSON parseable responses.

82

83

Parameters:

84

- uri: Resource URI that returned non-JSON

85

- response: HTTP response object

86

"""

87

88

@property

89

def uri(self) -> str:

90

"""Resource URI"""

91

92

@property

93

def response(self):

94

"""HTTP response object"""

95

```

96

97

### Navigation Traversal Errors

98

99

Exception for invalid navigation operations and traversal failures.

100

101

```python { .api }

102

class OffTheRailsException(TypeError):

103

def __init__(self, traversal: list, index: int, intermediates: list, exception: Exception):

104

"""

105

Raised for invalid navigation traversals.

106

107

Parameters:

108

- traversal: The attempted traversal path

109

- index: Where in the traversal the failure occurred

110

- intermediates: Successfully traversed navigators

111

- exception: Original exception that caused the failure

112

"""

113

114

@property

115

def traversal(self) -> list:

116

"""The attempted traversal path"""

117

118

@property

119

def index(self) -> int:

120

"""Index where traversal failed"""

121

122

@property

123

def intermediates(self) -> list:

124

"""Intermediate navigators before failure"""

125

126

@property

127

def exception(self) -> Exception:

128

"""Original exception"""

129

```

130

131

## Error Handling Patterns

132

133

### Basic Error Handling

134

135

```python

136

from restnavigator import Navigator

137

from restnavigator.exc import HALNavigatorError

138

139

api = Navigator.hal('https://api.example.com/')

140

141

try:

142

user = api['users', 'nonexistent']()

143

except HALNavigatorError as e:

144

print(f"HTTP Error {e.status}: {e.message}")

145

print(f"Failed URL: {e.nav.uri}")

146

print(f"Response: {e.response.text}")

147

```

148

149

### Graceful Error Handling

150

151

```python

152

# Disable automatic exception raising

153

result = api['users'].create(invalid_data, raise_exc=False)

154

155

if not result: # Check if operation succeeded

156

status_code, reason = result.status

157

print(f"Creation failed: {status_code} {reason}")

158

159

# Access error details from response

160

if hasattr(result, 'state') and result.state:

161

print("Error details:", result.state)

162

```

163

164

### Specific Status Code Handling

165

166

```python

167

try:

168

user_data = api['users', user_id]()

169

except HALNavigatorError as e:

170

if e.status == 404:

171

print("User not found")

172

# Handle missing resource

173

elif e.status == 403:

174

print("Access denied")

175

# Handle permission error

176

elif e.status >= 500:

177

print("Server error, retrying...")

178

# Handle server errors

179

else:

180

print(f"Unexpected error: {e.status}")

181

raise

182

```

183

184

### Navigation Error Handling

185

186

```python

187

from restnavigator.exc import OffTheRailsException

188

189

try:

190

# Complex navigation that might fail

191

data = api['users', 0, 'posts', 'comments', 'invalid']()

192

except OffTheRailsException as e:

193

print(f"Navigation failed at step {e.index}")

194

print(f"Attempted path: {e.traversal}")

195

print(f"Successfully navigated: {len(e.intermediates)} steps")

196

print(f"Error: {e.exception}")

197

198

# Work with successful intermediate results

199

if e.intermediates:

200

last_successful = e.intermediates[-1]

201

print(f"Last successful resource: {last_successful.uri}")

202

```

203

204

### JSON Parsing Error Handling

205

206

```python

207

from restnavigator.exc import UnexpectedlyNotJSON

208

209

try:

210

# Request might return non-JSON (e.g., HTML error page)

211

data = api['csv-export']()

212

except UnexpectedlyNotJSON as e:

213

print(f"Expected JSON from {e.uri}")

214

print(f"Got content type: {e.response.headers.get('content-type')}")

215

216

# Handle non-JSON response appropriately

217

if 'text/csv' in e.response.headers.get('content-type', ''):

218

csv_data = e.response.text

219

# Process CSV data

220

else:

221

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

222

```

223

224

### URL Validation Error Handling

225

226

```python

227

from restnavigator.exc import WileECoyoteException, ZachMorrisException

228

229

try:

230

# This might have URL issues

231

api = Navigator.hal('invalid-url')

232

except WileECoyoteException:

233

print("Invalid URL scheme - make sure URL starts with http:// or https://")

234

except ZachMorrisException:

235

print("Multiple conflicting schemes in URL")

236

```

237

238

### Resource State Error Handling

239

240

```python

241

from restnavigator.exc import NoResponseError

242

243

# Create navigator but don't fetch yet

244

user = api['users', user_id]

245

246

try:

247

# This will fail because we haven't fetched the resource

248

print(user.state)

249

except NoResponseError:

250

print("Resource not yet fetched")

251

# Fetch the resource first

252

user_data = user()

253

print(user.state) # Now this works

254

```

255

256

### Comprehensive Error Handling Strategy

257

258

```python

259

def safe_api_call(navigator_func):

260

"""Wrapper for safe API calls with comprehensive error handling"""

261

try:

262

return navigator_func()

263

except HALNavigatorError as e:

264

if e.status in [401, 403]:

265

# Authentication/authorization error

266

handle_auth_error(e)

267

elif e.status == 404:

268

# Resource not found

269

return None

270

elif e.status == 429:

271

# Rate limiting

272

handle_rate_limit(e)

273

elif e.status >= 500:

274

# Server error

275

handle_server_error(e)

276

else:

277

# Other HTTP errors

278

log_error(f"HTTP {e.status}: {e.message}")

279

raise

280

except OffTheRailsException as e:

281

# Navigation error

282

log_error(f"Navigation failed: {e.traversal}")

283

return None

284

except UnexpectedlyNotJSON as e:

285

# Content type error

286

log_error(f"Non-JSON response from {e.uri}")

287

return e.response.text

288

except (WileECoyoteException, ZachMorrisException) as e:

289

# URL validation error

290

log_error(f"Invalid URL: {e}")

291

raise ValueError("Invalid API URL configuration")

292

except NoResponseError:

293

# Resource state error

294

log_error("Attempted to access unfetched resource")

295

return None

296

297

# Usage

298

user_data = safe_api_call(lambda: api['users', user_id]())

299

if user_data is None:

300

print("Failed to retrieve user data")

301

```

302

303

### Error Context and Debugging

304

305

```python

306

def debug_error(exception):

307

"""Extract comprehensive error information for debugging"""

308

if isinstance(exception, HALNavigatorError):

309

print(f"HTTP Error: {exception.status}")

310

print(f"URL: {exception.nav.uri}")

311

print(f"Message: {exception.message}")

312

print(f"Response Headers: {dict(exception.response.headers)}")

313

print(f"Response Body: {exception.response.text}")

314

315

# Check request details

316

request = exception.response.request

317

print(f"Request Method: {request.method}")

318

print(f"Request Headers: {dict(request.headers)}")

319

if request.body:

320

print(f"Request Body: {request.body}")

321

322

elif isinstance(exception, OffTheRailsException):

323

print(f"Navigation Error at step {exception.index}")

324

print(f"Full path: {exception.traversal}")

325

print(f"Completed steps: {len(exception.intermediates)}")

326

for i, nav in enumerate(exception.intermediates):

327

print(f" Step {i}: {nav.uri}")

328

print(f"Underlying error: {exception.exception}")

329

330

# Usage in exception handler

331

try:

332

data = api['complex', 'navigation', 'path']()

333

except Exception as e:

334

debug_error(e)

335

raise

336

```