or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-configuration.mddata-filtering.mderror-handling.mdindex.mdrecord-modes.mdrequest-matching.mdrequest-response.mdserialization.mdtest-integration.md

error-handling.mddocs/

0

# Error Handling

1

2

Exception classes for handling VCR-specific errors during recording and playback operations. VCR.py provides specific exception types to help diagnose and handle different error conditions.

3

4

## Capabilities

5

6

### CannotOverwriteExistingCassetteException

7

8

Exception raised when attempting to record new interactions in a cassette that cannot be overwritten based on the current record mode.

9

10

```python { .api }

11

class CannotOverwriteExistingCassetteException(Exception):

12

"""

13

Raised when VCR cannot overwrite an existing cassette.

14

15

This typically occurs in 'once' record mode when a test tries to make

16

HTTP requests that aren't already recorded in the cassette.

17

18

Attributes:

19

cassette: The cassette that couldn't be overwritten

20

failed_request: The request that couldn't be matched/recorded

21

"""

22

23

def __init__(self, cassette, failed_request):

24

"""

25

Initialize exception with cassette and request details.

26

27

Args:

28

cassette: Cassette object that couldn't be overwritten

29

failed_request: Request object that caused the failure

30

"""

31

32

cassette: Cassette

33

failed_request: Request

34

```

35

36

### UnhandledHTTPRequestError

37

38

Exception raised when a cassette doesn't contain a recorded response for the requested HTTP interaction.

39

40

```python { .api }

41

class UnhandledHTTPRequestError(KeyError):

42

"""

43

Raised when a cassette does not contain the request we want.

44

45

This occurs when VCR intercepts an HTTP request but cannot find

46

a matching recorded interaction in the current cassette.

47

"""

48

```

49

50

## Usage Examples

51

52

### Handling Record Mode Violations

53

54

```python

55

import vcr

56

import requests

57

from vcr.errors import CannotOverwriteExistingCassetteException

58

59

@vcr.use_cassette('existing.yaml', record_mode='once')

60

def test_with_error_handling():

61

"""Test that handles cassette overwrite errors."""

62

try:

63

# This request must already exist in the cassette

64

response = requests.get('https://api.example.com/existing-endpoint')

65

assert response.status_code == 200

66

67

# This new request will cause an error in 'once' mode

68

response = requests.get('https://api.example.com/new-endpoint')

69

70

except CannotOverwriteExistingCassetteException as e:

71

print(f"Cannot add new request to cassette: {e.failed_request.uri}")

72

print(f"Cassette path: {e.cassette._path}")

73

print(f"Record mode: {e.cassette.record_mode}")

74

75

# Handle the error - perhaps switch to NEW_EPISODES mode

76

# or update the test to use existing endpoints

77

pass

78

```

79

80

### Handling Missing Interactions

81

82

```python

83

import vcr

84

import requests

85

from vcr.errors import UnhandledHTTPRequestError

86

87

@vcr.use_cassette('partial.yaml', record_mode='none')

88

def test_missing_interaction():

89

"""Test that handles missing recorded interactions."""

90

try:

91

# This request must exist in the cassette

92

response = requests.get('https://api.example.com/recorded-endpoint')

93

assert response.status_code == 200

94

95

# This request is not in the cassette

96

response = requests.get('https://api.example.com/missing-endpoint')

97

98

except UnhandledHTTPRequestError as e:

99

print(f"No recorded interaction for request: {e}")

100

101

# Handle missing interaction

102

# - Add the interaction to the cassette

103

# - Mock the response

104

# - Skip the test

105

# - Use a different cassette

106

pass

107

```

108

109

### Comprehensive Error Handling

110

111

```python

112

import vcr

113

import requests

114

from vcr.errors import CannotOverwriteExistingCassetteException, UnhandledHTTPRequestError

115

116

class APITestCase:

117

"""Test case with comprehensive VCR error handling."""

118

119

def make_api_request(self, url, method='GET', **kwargs):

120

"""Make API request with VCR error handling."""

121

try:

122

response = requests.request(method, url, **kwargs)

123

return response

124

125

except CannotOverwriteExistingCassetteException as e:

126

# Handle cassette overwrite errors

127

self.handle_cassette_overwrite_error(e)

128

129

except UnhandledHTTPRequestError as e:

130

# Handle missing interaction errors

131

self.handle_missing_interaction_error(e)

132

133

except Exception as e:

134

# Handle other potential errors

135

self.handle_general_error(e)

136

137

def handle_cassette_overwrite_error(self, error):

138

"""Handle cassette overwrite errors."""

139

print(f"Cassette overwrite error:")

140

print(f" Request: {error.failed_request.method} {error.failed_request.uri}")

141

print(f" Cassette: {error.cassette._path}")

142

print(f" Record mode: {error.cassette.record_mode}")

143

144

# Suggest solutions

145

if error.cassette.record_mode == 'once':

146

print(" Solution: Change record_mode to 'new_episodes' or add request to cassette")

147

148

# Find similar requests for debugging

149

if hasattr(error.cassette, 'find_requests_with_most_matches'):

150

matches = error.cassette.find_requests_with_most_matches(error.failed_request)

151

if matches:

152

print(f" Similar requests found: {len(matches)}")

153

for i, (request, succeeded, failed) in enumerate(matches[:3]):

154

print(f" {i+1}. {request.method} {request.uri}")

155

print(f" Matched: {succeeded}")

156

print(f" Failed: {[f[0] for f in failed]}")

157

158

def handle_missing_interaction_error(self, error):

159

"""Handle missing interaction errors."""

160

print(f"Missing interaction error: {error}")

161

print(" Possible solutions:")

162

print(" - Record the interaction by changing record_mode")

163

print(" - Add the interaction manually to the cassette")

164

print(" - Use a different cassette that contains the interaction")

165

print(" - Mock the response instead of using VCR")

166

167

def handle_general_error(self, error):

168

"""Handle other VCR-related errors."""

169

print(f"VCR error: {type(error).__name__}: {error}")

170

```

171

172

### Debug Mode Error Handling

173

174

```python

175

import logging

176

import vcr

177

from vcr.errors import CannotOverwriteExistingCassetteException, UnhandledHTTPRequestError

178

179

# Enable VCR debug logging

180

logging.basicConfig(level=logging.DEBUG)

181

vcr_log = logging.getLogger('vcr')

182

vcr_log.setLevel(logging.DEBUG)

183

184

def debug_vcr_errors(test_function):

185

"""Decorator to add debug information to VCR errors."""

186

def wrapper(*args, **kwargs):

187

try:

188

return test_function(*args, **kwargs)

189

except CannotOverwriteExistingCassetteException as e:

190

print("\n=== VCR CASSETTE OVERWRITE ERROR DEBUG ===")

191

print(f"Test function: {test_function.__name__}")

192

print(f"Failed request: {e.failed_request.method} {e.failed_request.uri}")

193

print(f"Request headers: {dict(e.failed_request.headers)}")

194

print(f"Request body: {e.failed_request.body}")

195

print(f"Cassette path: {e.cassette._path}")

196

print(f"Cassette record mode: {e.cassette.record_mode}")

197

print(f"Cassette interactions count: {len(e.cassette.responses)}")

198

raise

199

except UnhandledHTTPRequestError as e:

200

print("\n=== VCR UNHANDLED REQUEST ERROR DEBUG ===")

201

print(f"Test function: {test_function.__name__}")

202

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

203

raise

204

return wrapper

205

206

@debug_vcr_errors

207

@vcr.use_cassette('debug.yaml')

208

def test_with_debug_info():

209

"""Test with enhanced error debugging."""

210

response = requests.get('https://api.example.com/data')

211

```

212

213

### Custom Error Recovery

214

215

```python

216

import vcr

217

import requests

218

from vcr.errors import CannotOverwriteExistingCassetteException, UnhandledHTTPRequestError

219

220

class RecoveringVCRTest:

221

"""Test class that attempts to recover from VCR errors."""

222

223

def __init__(self, cassette_path):

224

self.cassette_path = cassette_path

225

self.backup_responses = {}

226

227

def make_request_with_recovery(self, url, method='GET', **kwargs):

228

"""Make request with automatic error recovery."""

229

230

# First attempt: Use strict mode

231

try:

232

with vcr.use_cassette(self.cassette_path, record_mode='once'):

233

return requests.request(method, url, **kwargs)

234

235

except (CannotOverwriteExistingCassetteException, UnhandledHTTPRequestError):

236

print(f"VCR error for {method} {url}, attempting recovery...")

237

238

# Second attempt: Allow new episodes

239

try:

240

with vcr.use_cassette(self.cassette_path, record_mode='new_episodes'):

241

return requests.request(method, url, **kwargs)

242

243

except Exception as e:

244

print(f"VCR recovery failed: {e}")

245

246

# Final attempt: Use backup response or real request

247

return self.get_backup_response(url, method) or requests.request(method, url, **kwargs)

248

249

def get_backup_response(self, url, method):

250

"""Get backup response for URL if available."""

251

key = f"{method}:{url}"

252

return self.backup_responses.get(key)

253

254

def set_backup_response(self, url, method, response):

255

"""Set backup response for URL."""

256

key = f"{method}:{url}"

257

self.backup_responses[key] = response

258

```

259

260

### Error Analysis and Reporting

261

262

```python

263

from collections import defaultdict

264

import vcr

265

from vcr.errors import CannotOverwriteExistingCassetteException, UnhandledHTTPRequestError

266

267

class VCRErrorReporter:

268

"""Collect and analyze VCR errors across test runs."""

269

270

def __init__(self):

271

self.errors = defaultdict(list)

272

273

def record_error(self, error, context=None):

274

"""Record an error for later analysis."""

275

error_type = type(error).__name__

276

error_info = {

277

'error': str(error),

278

'context': context,

279

}

280

281

if isinstance(error, CannotOverwriteExistingCassetteException):

282

error_info.update({

283

'cassette_path': error.cassette._path,

284

'record_mode': error.cassette.record_mode,

285

'failed_request_uri': error.failed_request.uri,

286

'failed_request_method': error.failed_request.method,

287

})

288

elif isinstance(error, UnhandledHTTPRequestError):

289

error_info.update({

290

'missing_request': str(error),

291

})

292

293

self.errors[error_type].append(error_info)

294

295

def generate_report(self):

296

"""Generate error analysis report."""

297

print("=== VCR ERROR ANALYSIS REPORT ===")

298

299

for error_type, error_list in self.errors.items():

300

print(f"\n{error_type}: {len(error_list)} occurrences")

301

302

if error_type == 'CannotOverwriteExistingCassetteException':

303

# Group by cassette path

304

by_cassette = defaultdict(list)

305

for error in error_list:

306

by_cassette[error['cassette_path']].append(error)

307

308

for cassette_path, cassette_errors in by_cassette.items():

309

print(f" Cassette: {cassette_path}")

310

print(f" Errors: {len(cassette_errors)}")

311

for error in cassette_errors[:3]: # Show first 3

312

print(f" {error['failed_request_method']} {error['failed_request_uri']}")

313

314

elif error_type == 'UnhandledHTTPRequestError':

315

# Group by missing request pattern

316

for error in error_list[:3]: # Show first 3

317

print(f" Missing: {error['missing_request']}")

318

319

# Usage

320

reporter = VCRErrorReporter()

321

322

def test_with_error_reporting():

323

try:

324

# Your VCR test code here

325

pass

326

except (CannotOverwriteExistingCassetteException, UnhandledHTTPRequestError) as e:

327

reporter.record_error(e, context={'test': 'test_with_error_reporting'})

328

raise

329

330

# At end of test run

331

reporter.generate_report()

332

```