or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

channel-management.mddns-queries.mderror-handling.mdhost-resolution.mdindex.mdutilities.md

error-handling.mddocs/

0

# Error Handling and Constants

1

2

Comprehensive error handling for DNS operations including error codes, utility functions, and debugging support. All pycares operations use consistent error reporting through callback functions and exception classes.

3

4

## Capabilities

5

6

### Error Codes

7

8

DNS resolution can fail for various reasons. pycares provides detailed error codes to help diagnose and handle different failure scenarios.

9

10

```python { .api }

11

# Success code

12

ARES_SUCCESS = 0

13

14

# DNS-specific errors

15

ARES_ENODATA = 1 # No data in DNS response

16

ARES_EFORMERR = 2 # DNS format error

17

ARES_ESERVFAIL = 3 # DNS server failure

18

ARES_ENOTFOUND = 4 # Domain name not found

19

ARES_ENOTIMP = 5 # Query type not implemented

20

ARES_EREFUSED = 6 # DNS query refused by server

21

ARES_EBADQUERY = 7 # Malformed DNS query

22

ARES_EBADNAME = 8 # Malformed domain name

23

ARES_EBADFAMILY = 9 # Bad address family

24

ARES_EBADRESP = 10 # Bad DNS response

25

ARES_ECONNREFUSED = 11 # Connection refused

26

ARES_ETIMEOUT = 12 # Query timeout

27

ARES_EOF = 13 # End of file

28

ARES_EFILE = 14 # File I/O error

29

ARES_ENOMEM = 15 # Out of memory

30

ARES_EDESTRUCTION = 16 # Channel destroyed

31

ARES_EBADSTR = 17 # Bad string

32

ARES_EBADFLAGS = 18 # Bad flags

33

ARES_ENONAME = 19 # No name

34

ARES_EBADHINTS = 20 # Bad hints

35

ARES_ENOTINITIALIZED = 21 # Not initialized

36

ARES_ELOADIPHLPAPI = 22 # Failed to load iphlpapi

37

ARES_EADDRGETNETWORKPARAMS = 23 # Failed to get network parameters

38

ARES_ECANCELLED = 24 # Query cancelled

39

ARES_ESERVICE = 25 # Service error

40

```

41

42

### Error Code Mapping

43

44

Mapping dictionary for converting error codes to symbolic names.

45

46

```python { .api }

47

errorcode: dict[int, str] # Maps error codes to string names

48

```

49

50

**Usage Example:**

51

52

```python

53

import pycares.errno

54

55

# Print all available error codes

56

for code, name in pycares.errno.errorcode.items():

57

print(f'{code}: {name}')

58

59

# Check if an error code is known

60

error_code = 4

61

if error_code in pycares.errno.errorcode:

62

print(f'Error {error_code} is {pycares.errno.errorcode[error_code]}')

63

```

64

65

### Error Messages

66

67

Convert error codes to human-readable error messages.

68

69

```python { .api }

70

def strerror(code: int) -> str:

71

"""

72

Get human-readable error message for an error code.

73

74

Args:

75

code: int - Error code from callback or exception

76

77

Returns:

78

str: Human-readable error message

79

"""

80

```

81

82

**Usage Example:**

83

84

```python

85

import pycares

86

import pycares.errno

87

88

def query_callback(result, error):

89

if error is not None:

90

error_name = pycares.errno.errorcode.get(error, f'UNKNOWN_{error}')

91

error_msg = pycares.errno.strerror(error)

92

print(f'DNS query failed: {error_name} - {error_msg}')

93

return

94

95

print(f'Query successful: {result}')

96

97

channel = pycares.Channel()

98

channel.query('nonexistent.invalid', pycares.QUERY_TYPE_A, query_callback)

99

```

100

101

## Exception Classes

102

103

### AresError

104

105

Primary exception class for c-ares related errors.

106

107

```python { .api }

108

class AresError(Exception):

109

"""

110

Exception raised for c-ares library errors.

111

112

This exception is raised for synchronous errors during channel creation,

113

configuration, or other immediate operations. Asynchronous query errors

114

are reported via callback functions, not exceptions.

115

"""

116

```

117

118

**Usage Example:**

119

120

```python

121

import pycares

122

123

try:

124

# This might raise AresError if initialization fails

125

channel = pycares.Channel(servers=['invalid.ip.address'])

126

except pycares.AresError as e:

127

print(f'Failed to create channel: {e}')

128

129

try:

130

channel = pycares.Channel()

131

# This might raise AresError if reinit fails

132

channel.reinit()

133

except pycares.AresError as e:

134

print(f'Failed to reinitialize channel: {e}')

135

```

136

137

## Error Handling Patterns

138

139

### Basic Error Handling

140

141

```python

142

import pycares

143

import pycares.errno

144

145

def handle_dns_result(result, error):

146

"""Standard DNS result handler with error checking."""

147

if error is not None:

148

if error == pycares.errno.ARES_ENOTFOUND:

149

print('Domain not found')

150

elif error == pycares.errno.ARES_ETIMEOUT:

151

print('Query timed out')

152

elif error == pycares.errno.ARES_ESERVFAIL:

153

print('DNS server failure')

154

else:

155

print(f'DNS error: {pycares.errno.strerror(error)}')

156

return

157

158

# Process successful result

159

print(f'DNS query successful: {result}')

160

161

channel = pycares.Channel()

162

channel.query('example.com', pycares.QUERY_TYPE_A, handle_dns_result)

163

```

164

165

### Retry Logic

166

167

```python

168

class DNSResolver:

169

def __init__(self, max_retries=3):

170

self.channel = pycares.Channel(timeout=5.0, tries=2)

171

self.max_retries = max_retries

172

173

def query_with_retry(self, name, query_type, callback, retries=0):

174

def retry_callback(result, error):

175

if error is not None and retries < self.max_retries:

176

# Retry on timeout or server failure

177

if error in (pycares.errno.ARES_ETIMEOUT, pycares.errno.ARES_ESERVFAIL):

178

print(f'Retrying query ({retries + 1}/{self.max_retries})')

179

self.query_with_retry(name, query_type, callback, retries + 1)

180

return

181

182

# Final result (success or non-retryable error)

183

callback(result, error)

184

185

self.channel.query(name, query_type, retry_callback)

186

187

resolver = DNSResolver()

188

resolver.query_with_retry('google.com', pycares.QUERY_TYPE_A, handle_dns_result)

189

```

190

191

### Error Categorization

192

193

```python

194

import pycares.errno

195

196

def categorize_error(error_code):

197

"""Categorize DNS errors for appropriate handling."""

198

if error_code == pycares.errno.ARES_SUCCESS:

199

return 'success'

200

elif error_code in (pycares.errno.ARES_ENOTFOUND, pycares.errno.ARES_ENODATA):

201

return 'not_found'

202

elif error_code in (pycares.errno.ARES_ETIMEOUT, pycares.errno.ARES_ECONNREFUSED):

203

return 'network'

204

elif error_code in (pycares.errno.ARES_ESERVFAIL, pycares.errno.ARES_EREFUSED):

205

return 'server'

206

elif error_code in (pycares.errno.ARES_EBADNAME, pycares.errno.ARES_EBADQUERY):

207

return 'invalid_input'

208

elif error_code == pycares.errno.ARES_ECANCELLED:

209

return 'cancelled'

210

else:

211

return 'unknown'

212

213

def handle_categorized_error(result, error):

214

if error is None:

215

print(f'Success: {result}')

216

return

217

218

category = categorize_error(error)

219

error_msg = pycares.errno.strerror(error)

220

221

if category == 'not_found':

222

print(f'Domain not found: {error_msg}')

223

elif category == 'network':

224

print(f'Network error (check connectivity): {error_msg}')

225

elif category == 'server':

226

print(f'DNS server error (try different server): {error_msg}')

227

elif category == 'invalid_input':

228

print(f'Invalid input (check domain name): {error_msg}')

229

elif category == 'cancelled':

230

print('Query was cancelled')

231

else:

232

print(f'Unknown error: {error_msg}')

233

```

234

235

### Timeout Handling

236

237

```python

238

import time

239

import threading

240

import pycares

241

242

class TimeoutHandler:

243

def __init__(self, channel, timeout_seconds=10):

244

self.channel = channel

245

self.timeout_seconds = timeout_seconds

246

self.active_queries = set()

247

248

def query_with_timeout(self, name, query_type, callback):

249

query_id = id(callback) # Simple query identifier

250

self.active_queries.add(query_id)

251

252

# Start timeout timer

253

def timeout_callback():

254

if query_id in self.active_queries:

255

self.active_queries.remove(query_id)

256

callback(None, pycares.errno.ARES_ETIMEOUT)

257

258

timer = threading.Timer(self.timeout_seconds, timeout_callback)

259

timer.start()

260

261

# Wrap original callback

262

def wrapped_callback(result, error):

263

if query_id in self.active_queries:

264

timer.cancel()

265

self.active_queries.remove(query_id)

266

callback(result, error)

267

268

self.channel.query(name, query_type, wrapped_callback)

269

270

# Usage

271

handler = TimeoutHandler(pycares.Channel(), timeout_seconds=5)

272

handler.query_with_timeout('slow-server.com', pycares.QUERY_TYPE_A, handle_dns_result)

273

```

274

275

## Debugging Support

276

277

### Verbose Error Logging

278

279

```python

280

import logging

281

import pycares.errno

282

283

logging.basicConfig(level=logging.DEBUG)

284

logger = logging.getLogger(__name__)

285

286

def debug_callback(name, query_type):

287

"""Create a callback with debug logging."""

288

def callback(result, error):

289

if error is not None:

290

error_name = pycares.errno.errorcode.get(error, f'UNKNOWN_{error}')

291

error_msg = pycares.errno.strerror(error)

292

logger.error(f'Query failed: {name} ({query_type}) - {error_name}: {error_msg}')

293

else:

294

logger.info(f'Query successful: {name} ({query_type}) - {len(result) if isinstance(result, list) else 1} records')

295

logger.debug(f'Result: {result}')

296

return callback

297

298

channel = pycares.Channel()

299

channel.query('google.com', pycares.QUERY_TYPE_A, debug_callback('google.com', 'A'))

300

```

301

302

### Error Statistics

303

304

```python

305

class ErrorStats:

306

def __init__(self):

307

self.error_counts = {}

308

self.success_count = 0

309

310

def record_result(self, error):

311

if error is None:

312

self.success_count += 1

313

else:

314

error_name = pycares.errno.errorcode.get(error, f'UNKNOWN_{error}')

315

self.error_counts[error_name] = self.error_counts.get(error_name, 0) + 1

316

317

def print_stats(self):

318

total = self.success_count + sum(self.error_counts.values())

319

print(f'DNS Query Statistics (Total: {total})')

320

print(f' Successful: {self.success_count}')

321

for error_name, count in sorted(self.error_counts.items()):

322

print(f' {error_name}: {count}')

323

324

stats = ErrorStats()

325

326

def stats_callback(result, error):

327

stats.record_result(error)

328

if error is None:

329

print(f'Success: {result}')

330

else:

331

print(f'Error: {pycares.errno.strerror(error)}')

332

333

# After running queries...

334

stats.print_stats()

335

```

336

337

## Common Error Scenarios

338

339

### Network Connectivity Issues

340

341

```python

342

# DNS server unreachable

343

# Error: ARES_ECONNREFUSED or ARES_ETIMEOUT

344

345

# Firewall blocking DNS

346

# Error: ARES_ETIMEOUT

347

348

# No internet connection

349

# Error: ARES_ECONNREFUSED or ARES_ETIMEOUT

350

```

351

352

### DNS Configuration Problems

353

354

```python

355

# Invalid DNS server configured

356

# Error: ARES_ESERVFAIL or ARES_ETIMEOUT

357

358

# DNS server not responding

359

# Error: ARES_ETIMEOUT

360

361

# DNS server refusing queries

362

# Error: ARES_EREFUSED

363

```

364

365

### Query Issues

366

367

```python

368

# Domain doesn't exist

369

# Error: ARES_ENOTFOUND

370

371

# Record type doesn't exist for domain

372

# Error: ARES_ENODATA

373

374

# Malformed domain name

375

# Error: ARES_EBADNAME

376

377

# Invalid query parameters

378

# Error: ARES_EBADQUERY

379

```

380

381

All error handling in pycares follows the pattern of passing error codes to callback functions rather than raising exceptions for asynchronous operations. Synchronous configuration errors raise AresError exceptions.