0
# Error Handling
1
2
Exception classes and error handling patterns for configuration parsing, validation failures, and format errors with detailed error reporting and debugging information. Understanding dynaconf's error handling helps debug configuration issues and implement robust error recovery.
3
4
## Capabilities
5
6
### Validation Errors
7
8
Handle configuration validation failures with detailed error information.
9
10
```python { .api }
11
class ValidationError(Exception):
12
"""Exception raised when configuration validation fails."""
13
def __init__(self, message: str, details=None): ...
14
15
@property
16
def message(self) -> str:
17
"""Primary error message."""
18
...
19
20
@property
21
def details(self) -> list:
22
"""List of detailed error information including failed validators."""
23
...
24
```
25
26
Usage examples:
27
28
```python
29
from dynaconf import Dynaconf, Validator, ValidationError
30
31
settings = Dynaconf(
32
validators=[
33
Validator("DATABASE_URL", must_exist=True),
34
Validator("PORT", cast=int, gte=1000, lte=65535),
35
Validator("DEBUG", cast=bool),
36
]
37
)
38
39
try:
40
settings.validators.validate()
41
except ValidationError as e:
42
print(f"Validation failed: {e.message}")
43
44
# Access detailed error information
45
for detail in e.details:
46
print(f" Failed validator: {detail['validator']}")
47
print(f" Key: {detail['key']}")
48
print(f" Issue: {detail['issue']}")
49
50
# Log for debugging
51
import logging
52
logging.error(f"Configuration validation error: {e}")
53
```
54
55
### Format Errors
56
57
Handle errors in configuration value formatting and lazy evaluation.
58
59
```python { .api }
60
class DynaconfFormatError(Exception):
61
"""Exception raised when formatting lazy variables fails."""
62
pass
63
```
64
65
Usage examples:
66
67
```python
68
from dynaconf import Dynaconf, DynaconfFormatError
69
70
# Configuration with invalid formatting
71
# settings.toml:
72
# message = "Hello @{UNDEFINED_VAR}!"
73
74
settings = Dynaconf(settings_files=["settings.toml"])
75
76
try:
77
formatted_message = settings.message
78
except DynaconfFormatError as e:
79
print(f"Format error in configuration: {e}")
80
81
# Provide fallback value
82
formatted_message = "Hello World!"
83
84
# Or use safe access
85
formatted_message = settings.get("message", "Default message")
86
```
87
88
### Parse Errors
89
90
Handle errors in parsing @cast directives and type conversions.
91
92
```python { .api }
93
class DynaconfParseError(Exception):
94
"""Exception raised when parsing @cast directives fails."""
95
pass
96
```
97
98
Usage examples:
99
100
```python
101
from dynaconf import Dynaconf, DynaconfParseError
102
103
# Configuration with invalid casting
104
# settings.toml:
105
# port = "@int invalid_number"
106
# config = "@json {invalid json}"
107
108
settings = Dynaconf(settings_files=["settings.toml"])
109
110
try:
111
port = settings.port
112
except DynaconfParseError as e:
113
print(f"Parse error: {e}")
114
# Use default value
115
port = 8000
116
117
try:
118
config_data = settings.config
119
except DynaconfParseError as e:
120
print(f"JSON parse error: {e}")
121
# Use empty dict as fallback
122
config_data = {}
123
```
124
125
## Error Handling Patterns
126
127
### Comprehensive Error Handling
128
129
Handle all dynaconf exceptions in a unified way.
130
131
```python
132
from dynaconf import (
133
Dynaconf, ValidationError, DynaconfFormatError,
134
DynaconfParseError, Validator
135
)
136
import logging
137
138
def create_robust_settings():
139
"""Create settings with comprehensive error handling."""
140
try:
141
settings = Dynaconf(
142
envvar_prefix="MYAPP",
143
settings_files=["config.toml", "local.yaml"],
144
environments=True,
145
validators=[
146
Validator("DATABASE_URL", must_exist=True),
147
Validator("PORT", cast=int, default=8000),
148
Validator("DEBUG", cast=bool, default=False),
149
]
150
)
151
152
# Validate configuration
153
settings.validators.validate()
154
155
return settings
156
157
except ValidationError as e:
158
logging.error(f"Configuration validation failed: {e.message}")
159
for detail in e.details:
160
logging.error(f" {detail}")
161
raise
162
163
except DynaconfFormatError as e:
164
logging.error(f"Configuration format error: {e}")
165
raise
166
167
except DynaconfParseError as e:
168
logging.error(f"Configuration parse error: {e}")
169
raise
170
171
except Exception as e:
172
logging.error(f"Unexpected configuration error: {e}")
173
raise
174
175
# Usage with error handling
176
try:
177
settings = create_robust_settings()
178
print("Configuration loaded successfully!")
179
except Exception as e:
180
print(f"Failed to load configuration: {e}")
181
# Use fallback configuration or exit gracefully
182
```
183
184
### Graceful Degradation
185
186
Implement fallback strategies when configuration loading fails.
187
188
```python
189
def get_settings_with_fallback():
190
"""Get settings with graceful degradation to defaults."""
191
default_settings = {
192
'DATABASE_URL': 'sqlite:///default.db',
193
'PORT': 8000,
194
'DEBUG': True,
195
'SECRET_KEY': 'development-only-key',
196
}
197
198
try:
199
# Try to load full configuration
200
settings = Dynaconf(
201
settings_files=["config.toml", "production.yaml"],
202
environments=True,
203
validators=[
204
Validator("DATABASE_URL", must_exist=True),
205
Validator("SECRET_KEY", must_exist=True),
206
]
207
)
208
209
settings.validators.validate()
210
return settings
211
212
except ValidationError as e:
213
print(f"Validation failed, using partial configuration: {e.message}")
214
215
# Create minimal settings with available values
216
settings = Dynaconf(environments=False)
217
218
# Apply defaults for missing required values
219
for key, default_value in default_settings.items():
220
if not settings.exists(key):
221
settings.set(key, default_value)
222
223
return settings
224
225
except (DynaconfFormatError, DynaconfParseError) as e:
226
print(f"Configuration parse error, using defaults: {e}")
227
228
# Return minimal settings with defaults
229
settings = Dynaconf(environments=False)
230
for key, value in default_settings.items():
231
settings.set(key, value)
232
233
return settings
234
235
# Safe configuration access
236
def safe_get_setting(settings, key, default=None, cast=None):
237
"""Safely get setting value with error handling."""
238
try:
239
return settings.get(key, default=default, cast=cast)
240
except (DynaconfFormatError, DynaconfParseError) as e:
241
logging.warning(f"Error accessing setting '{key}': {e}")
242
return default
243
```
244
245
### Environment-Specific Error Handling
246
247
Handle errors differently based on the environment.
248
249
```python
250
def handle_configuration_error(error, current_env="development"):
251
"""Handle configuration errors based on environment."""
252
253
if current_env == "production":
254
# In production, log error and exit
255
logging.critical(f"Production configuration error: {error}")
256
import sys
257
sys.exit(1)
258
259
elif current_env == "development":
260
# In development, warn and continue with defaults
261
logging.warning(f"Development configuration error: {error}")
262
print(f"Warning: {error}")
263
return True # Continue execution
264
265
elif current_env == "testing":
266
# In testing, use minimal configuration
267
logging.info(f"Testing configuration error (expected): {error}")
268
return True
269
270
else:
271
# Unknown environment, be conservative
272
logging.error(f"Configuration error in unknown environment: {error}")
273
raise error
274
275
def create_environment_aware_settings():
276
"""Create settings with environment-aware error handling."""
277
current_env = os.environ.get("APP_ENV", "development")
278
279
try:
280
settings = Dynaconf(
281
envvar_prefix="MYAPP",
282
settings_files=["config.toml"],
283
environments=True,
284
validators=[
285
Validator("DATABASE_URL", must_exist=True),
286
# More strict validation in production
287
Validator("SECRET_KEY", must_exist=(current_env == "production")),
288
]
289
)
290
291
settings.validators.validate()
292
return settings
293
294
except ValidationError as e:
295
if handle_configuration_error(e, current_env):
296
# Create minimal settings for non-production
297
return Dynaconf(environments=False)
298
else:
299
raise
300
```
301
302
### Custom Error Classes
303
304
Create application-specific error classes for better error handling.
305
306
```python
307
class ConfigurationError(Exception):
308
"""Base class for configuration-related errors."""
309
pass
310
311
class MissingRequiredSettingError(ConfigurationError):
312
"""Error for missing required configuration values."""
313
def __init__(self, key, environment=None):
314
self.key = key
315
self.environment = environment
316
env_msg = f" in environment '{environment}'" if environment else ""
317
super().__init__(f"Required setting '{key}' is missing{env_msg}")
318
319
class InvalidSettingValueError(ConfigurationError):
320
"""Error for invalid configuration values."""
321
def __init__(self, key, value, expected_type):
322
self.key = key
323
self.value = value
324
self.expected_type = expected_type
325
super().__init__(
326
f"Invalid value for '{key}': {value} (expected {expected_type})"
327
)
328
329
def validate_settings_strictly(settings):
330
"""Perform strict validation with custom error types."""
331
required_settings = {
332
"DATABASE_URL": str,
333
"SECRET_KEY": str,
334
"PORT": int,
335
"DEBUG": bool,
336
}
337
338
for key, expected_type in required_settings.items():
339
# Check existence
340
if not settings.exists(key):
341
raise MissingRequiredSettingError(key, settings.current_env)
342
343
# Check type
344
value = settings.get(key)
345
if not isinstance(value, expected_type):
346
raise InvalidSettingValueError(key, value, expected_type)
347
348
# Usage with custom error handling
349
try:
350
settings = Dynaconf(settings_files=["config.toml"])
351
validate_settings_strictly(settings)
352
except MissingRequiredSettingError as e:
353
print(f"Missing required setting: {e.key}")
354
# Handle missing setting
355
except InvalidSettingValueError as e:
356
print(f"Invalid setting value: {e.key} = {e.value}")
357
# Handle invalid value
358
except ConfigurationError as e:
359
print(f"Configuration error: {e}")
360
# Handle general configuration error
361
```
362
363
### Debugging Configuration Issues
364
365
Tools and patterns for debugging configuration problems.
366
367
```python
368
def debug_configuration_loading(settings):
369
"""Debug configuration loading issues."""
370
from dynaconf import inspect_settings, get_history
371
372
print("=== CONFIGURATION DEBUG REPORT ===")
373
print(f"Current environment: {settings.current_env}")
374
print(f"Loaded environments: {settings.loaded_envs}")
375
print()
376
377
# Show loading history
378
history = get_history(settings, include_internal=False)
379
print("Loading history:")
380
for entry in history[-10:]: # Last 10 entries
381
print(f" {entry['key']} = {entry['value']} (from {entry['loader']})")
382
print()
383
384
# Show validation issues
385
try:
386
settings.validators.validate()
387
print("All validations passed!")
388
except ValidationError as e:
389
print("Validation failures:")
390
for detail in e.details:
391
print(f" {detail}")
392
print()
393
394
# Show inspection report
395
inspect_settings(settings, dumper="yaml", print_report=True)
396
397
def trace_setting_value(settings, key):
398
"""Trace how a specific setting got its value."""
399
from dynaconf import get_history
400
401
print(f"=== TRACING SETTING: {key} ===")
402
403
# Current value
404
if settings.exists(key):
405
current_value = settings.get(key)
406
print(f"Current value: {current_value}")
407
else:
408
print("Setting does not exist")
409
return
410
411
# Loading history for this key
412
history = get_history(settings, key=key)
413
if history:
414
print("\nLoading history:")
415
for i, entry in enumerate(history):
416
print(f" {i+1}. {entry['value']} (from {entry['loader']})")
417
else:
418
print("No loading history found")
419
420
# Environment context
421
print(f"\nEnvironment: {settings.current_env}")
422
print(f"Available environments: {settings.loaded_envs}")
423
424
# Usage for debugging
425
def debug_settings_issue():
426
"""Debug a specific settings issue."""
427
try:
428
settings = Dynaconf(
429
settings_files=["config.toml", "local.yaml"],
430
environments=True
431
)
432
433
# Debug specific setting
434
trace_setting_value(settings, "DATABASE_URL")
435
436
# Full debug report
437
debug_configuration_loading(settings)
438
439
except Exception as e:
440
print(f"Error during debugging: {e}")
441
import traceback
442
traceback.print_exc()
443
```