0
# Exception Handling
1
2
Exception handling in Typesafe Config provides a comprehensive hierarchy of runtime exceptions for different error conditions. All exceptions extend ConfigException and provide detailed context information for debugging and error recovery.
3
4
## Exception Hierarchy
5
6
### ConfigException Base Class
7
8
Base class for all configuration-related exceptions.
9
10
```java { .api }
11
public abstract class ConfigException extends RuntimeException {
12
public ConfigException(String message);
13
public ConfigException(String message, Throwable cause);
14
public ConfigOrigin origin();
15
}
16
```
17
18
**Common Methods:**
19
- `getMessage()` - Human-readable error description
20
- `getCause()` - Underlying exception if applicable
21
- `origin()` - Configuration origin where error occurred
22
23
## Configuration Access Exceptions
24
25
### ConfigException.Missing
26
27
Thrown when a required configuration path is not found.
28
29
```java { .api }
30
public static class Missing extends ConfigException {
31
public String path();
32
}
33
```
34
35
**Usage Examples:**
36
37
```java
38
try {
39
String dbUrl = config.getString("database.url");
40
} catch (ConfigException.Missing e) {
41
String missingPath = e.path();
42
System.err.println("Missing required configuration: " + missingPath);
43
44
// Provide default or fail gracefully
45
String defaultUrl = "jdbc:h2:mem:default";
46
System.out.println("Using default database URL: " + defaultUrl);
47
}
48
```
49
50
### ConfigException.Null
51
52
Thrown when a configuration path exists but has a null value.
53
54
```java { .api }
55
public static class Null extends ConfigException.Missing {
56
// Inherits path() method from Missing
57
}
58
```
59
60
**Usage Examples:**
61
62
```java
63
try {
64
String apiKey = config.getString("api.key");
65
} catch (ConfigException.Null e) {
66
System.err.println("API key is explicitly set to null at: " + e.path());
67
// Handle null value scenario
68
} catch (ConfigException.Missing e) {
69
System.err.println("API key configuration is missing: " + e.path());
70
// Handle missing value scenario
71
}
72
```
73
74
### ConfigException.WrongType
75
76
Thrown when requesting a value with an incompatible type.
77
78
```java { .api }
79
public static class WrongType extends ConfigException {
80
public String path();
81
public ConfigValueType expected();
82
public ConfigValueType actual();
83
}
84
```
85
86
**Usage Examples:**
87
88
```java
89
try {
90
int port = config.getInt("server.port");
91
} catch (ConfigException.WrongType e) {
92
String path = e.path();
93
ConfigValueType expected = e.expected();
94
ConfigValueType actual = e.actual();
95
96
System.err.println(String.format(
97
"Type mismatch at %s: expected %s but got %s",
98
path, expected, actual
99
));
100
101
// Try alternative approaches
102
if (actual == ConfigValueType.STRING) {
103
try {
104
String portStr = config.getString(path);
105
int port = Integer.parseInt(portStr);
106
// Use parsed value
107
} catch (NumberFormatException nfe) {
108
// Handle invalid string format
109
}
110
}
111
}
112
```
113
114
## Parsing Exceptions
115
116
### ConfigException.Parse
117
118
Thrown when configuration files contain syntax errors.
119
120
```java { .api }
121
public static class Parse extends ConfigException {
122
public ConfigOrigin origin();
123
}
124
```
125
126
**Usage Examples:**
127
128
```java
129
try {
130
Config config = ConfigFactory.parseFile(configFile);
131
} catch (ConfigException.Parse e) {
132
ConfigOrigin origin = e.origin();
133
System.err.println(String.format(
134
"Parse error in %s at line %d: %s",
135
origin.filename(),
136
origin.lineNumber(),
137
e.getMessage()
138
));
139
140
// Handle parse error - maybe use defaults
141
Config defaultConfig = ConfigFactory.parseResources("reference.conf");
142
}
143
```
144
145
### ConfigException.IO
146
147
Thrown for I/O related errors when loading configuration files.
148
149
```java { .api }
150
public static class IO extends ConfigException {
151
public ConfigOrigin origin();
152
}
153
```
154
155
**Usage Examples:**
156
157
```java
158
try {
159
Config config = ConfigFactory.parseFile(new File("/etc/myapp/config.conf"));
160
} catch (ConfigException.IO e) {
161
System.err.println("I/O error reading configuration: " + e.getMessage());
162
163
// Try alternative locations
164
try {
165
config = ConfigFactory.parseFile(new File("./config.conf"));
166
} catch (ConfigException.IO e2) {
167
// Fall back to classpath resource
168
config = ConfigFactory.parseResources("application.conf");
169
}
170
}
171
```
172
173
## Resolution Exceptions
174
175
### ConfigException.UnresolvedSubstitution
176
177
Thrown when substitutions cannot be resolved.
178
179
```java { .api }
180
public static class UnresolvedSubstitution extends ConfigException {
181
public String path();
182
public String detail();
183
}
184
```
185
186
**Usage Examples:**
187
188
```java
189
try {
190
Config resolved = config.resolve();
191
} catch (ConfigException.UnresolvedSubstitution e) {
192
String path = e.path();
193
String detail = e.detail();
194
195
System.err.println(String.format(
196
"Cannot resolve substitution ${%s}: %s", path, detail
197
));
198
199
// Option 1: Allow partial resolution
200
Config partiallyResolved = config.resolve(
201
ConfigResolveOptions.defaults().setAllowUnresolved(true)
202
);
203
204
// Option 2: Provide missing values
205
Config withDefaults = config.withFallback(
206
ConfigFactory.parseString(path + " = \"default-value\"")
207
).resolve();
208
}
209
```
210
211
### ConfigException.NotResolved
212
213
Thrown when trying to access values from an unresolved configuration.
214
215
```java { .api }
216
public static class NotResolved extends ConfigException {
217
// Standard exception methods
218
}
219
```
220
221
**Usage Examples:**
222
223
```java
224
Config config = ConfigFactory.parseString("value = ${missing.ref}");
225
226
try {
227
// This will fail because config is not resolved
228
String value = config.getString("value");
229
} catch (ConfigException.NotResolved e) {
230
System.err.println("Configuration must be resolved first: " + e.getMessage());
231
232
// Resolve first
233
try {
234
Config resolved = config.resolve();
235
String value = resolved.getString("value");
236
} catch (ConfigException.UnresolvedSubstitution ue) {
237
// Handle unresolved substitutions
238
}
239
}
240
```
241
242
## Validation Exceptions
243
244
### ConfigException.ValidationFailed
245
246
Thrown by the `checkValid()` method when validation fails.
247
248
```java { .api }
249
public static class ValidationFailed extends ConfigException {
250
public Iterable<ValidationProblem> problems();
251
252
public static class ValidationProblem {
253
public String path();
254
public String problem();
255
public ConfigOrigin origin();
256
}
257
}
258
```
259
260
**Usage Examples:**
261
262
```java
263
Config reference = ConfigFactory.parseResources("reference.conf");
264
Config config = ConfigFactory.load();
265
266
try {
267
config.checkValid(reference);
268
} catch (ConfigException.ValidationFailed e) {
269
System.err.println("Configuration validation failed:");
270
271
for (ConfigException.ValidationProblem problem : e.problems()) {
272
String path = problem.path();
273
String description = problem.problem();
274
ConfigOrigin origin = problem.origin();
275
276
System.err.println(String.format(
277
" %s: %s (from %s:%d)",
278
path, description,
279
origin.filename(), origin.lineNumber()
280
));
281
}
282
283
// Handle validation failures
284
System.exit(1);
285
}
286
```
287
288
## Value-Specific Exceptions
289
290
### ConfigException.BadValue
291
292
Thrown when a configuration value is invalid for its intended use.
293
294
```java { .api }
295
public static class BadValue extends ConfigException {
296
public String path();
297
public String detail();
298
}
299
```
300
301
**Usage Examples:**
302
303
```java
304
try {
305
Duration timeout = config.getDuration("timeout");
306
} catch (ConfigException.BadValue e) {
307
System.err.println(String.format(
308
"Invalid duration value at %s: %s",
309
e.path(), e.detail()
310
));
311
312
// Use default duration
313
Duration defaultTimeout = Duration.ofSeconds(30);
314
}
315
316
try {
317
ConfigMemorySize heapSize = config.getMemorySize("jvm.heap");
318
} catch (ConfigException.BadValue e) {
319
System.err.println(String.format(
320
"Invalid memory size at %s: %s",
321
e.path(), e.detail()
322
));
323
}
324
```
325
326
### ConfigException.BadPath
327
328
Thrown when a path expression is malformed.
329
330
```java { .api }
331
public static class BadPath extends ConfigException {
332
public String path();
333
public String detail();
334
}
335
```
336
337
**Usage Examples:**
338
339
```java
340
try {
341
// Invalid path with special characters
342
boolean value = config.getBoolean("invalid..path");
343
} catch (ConfigException.BadPath e) {
344
System.err.println(String.format(
345
"Invalid path expression '%s': %s",
346
e.path(), e.detail()
347
));
348
349
// Use quoted path or fix the path
350
String quotedPath = ConfigUtil.joinPath("invalid", "", "path");
351
boolean value = config.getBoolean(quotedPath);
352
}
353
```
354
355
## JavaBean Exceptions
356
357
### ConfigException.BadBean
358
359
Thrown by ConfigBeanFactory when JavaBean creation fails.
360
361
```java { .api }
362
public static class BadBean extends ConfigException {
363
// Standard exception methods
364
}
365
```
366
367
**Usage Examples:**
368
369
```java
370
public class DatabaseConfig {
371
private String host;
372
private int port;
373
// getters and setters...
374
}
375
376
try {
377
DatabaseConfig dbConfig = ConfigBeanFactory.create(
378
config.getConfig("database"),
379
DatabaseConfig.class
380
);
381
} catch (ConfigException.BadBean e) {
382
System.err.println("Failed to create DatabaseConfig bean: " + e.getMessage());
383
384
// Manual configuration extraction
385
DatabaseConfig dbConfig = new DatabaseConfig();
386
dbConfig.setHost(config.getString("database.host"));
387
dbConfig.setPort(config.getInt("database.port"));
388
}
389
```
390
391
## Internal Exceptions
392
393
### ConfigException.BugOrBroken
394
395
Thrown for internal library errors that indicate bugs.
396
397
```java { .api }
398
public static class BugOrBroken extends ConfigException {
399
// Standard exception methods
400
}
401
```
402
403
**Usage:**
404
This exception indicates a bug in the Config library itself. If encountered, it should be reported to the library maintainers.
405
406
### ConfigException.Generic
407
408
Generic exception for miscellaneous error conditions.
409
410
```java { .api }
411
public static class Generic extends ConfigException {
412
// Standard exception methods
413
}
414
```
415
416
## Exception Handling Patterns
417
418
### Graceful Degradation
419
420
Handle configuration errors while maintaining application functionality.
421
422
```java
423
public class ConfigService {
424
private final Config config;
425
426
public ConfigService() {
427
Config primaryConfig = null;
428
429
try {
430
// Try primary configuration source
431
primaryConfig = ConfigFactory.load("application.conf");
432
} catch (ConfigException.IO e) {
433
System.err.println("Primary config not found, trying backup");
434
try {
435
primaryConfig = ConfigFactory.load("backup.conf");
436
} catch (ConfigException e2) {
437
System.err.println("Backup config failed, using defaults");
438
primaryConfig = ConfigFactory.parseResources("reference.conf");
439
}
440
}
441
442
this.config = primaryConfig;
443
}
444
445
public String getStringWithDefault(String path, String defaultValue) {
446
try {
447
return config.getString(path);
448
} catch (ConfigException.Missing | ConfigException.WrongType e) {
449
System.err.println("Using default for " + path + ": " + e.getMessage());
450
return defaultValue;
451
}
452
}
453
}
454
```
455
456
### Error Recovery
457
458
Implement recovery strategies for different exception types.
459
460
```java
461
public Config loadConfigWithRecovery(String resourceName) {
462
try {
463
return ConfigFactory.load(resourceName).resolve();
464
} catch (ConfigException.Parse e) {
465
// Parse error - try alternative formats
466
System.err.println("Parse error, trying JSON format: " + e.getMessage());
467
return ConfigFactory.parseResources(resourceName + ".json")
468
.resolve();
469
} catch (ConfigException.UnresolvedSubstitution e) {
470
// Resolution error - provide fallbacks
471
System.err.println("Resolution error, using partial config: " + e.getMessage());
472
return ConfigFactory.load(resourceName)
473
.resolve(ConfigResolveOptions.defaults().setAllowUnresolved(true));
474
} catch (ConfigException.IO e) {
475
// I/O error - use embedded defaults
476
System.err.println("I/O error, using embedded config: " + e.getMessage());
477
return ConfigFactory.parseResources("reference.conf").resolve();
478
}
479
}
480
```
481
482
### Validation and Error Reporting
483
484
Comprehensive error reporting for configuration issues.
485
486
```java
487
public void validateConfiguration(Config config) throws ConfigException {
488
List<String> errors = new ArrayList<>();
489
490
// Check required paths
491
String[] requiredPaths = {
492
"database.url", "database.username", "server.port"
493
};
494
495
for (String path : requiredPaths) {
496
try {
497
if (!config.hasPath(path)) {
498
errors.add("Missing required configuration: " + path);
499
} else if (config.getIsNull(path)) {
500
errors.add("Required configuration is null: " + path);
501
}
502
} catch (ConfigException e) {
503
errors.add("Error checking " + path + ": " + e.getMessage());
504
}
505
}
506
507
// Check value types and ranges
508
try {
509
int port = config.getInt("server.port");
510
if (port < 1 || port > 65535) {
511
errors.add("Server port must be between 1 and 65535, got: " + port);
512
}
513
} catch (ConfigException.Missing e) {
514
// Already handled above
515
} catch (ConfigException.WrongType e) {
516
errors.add("Server port must be a number: " + e.getMessage());
517
}
518
519
if (!errors.isEmpty()) {
520
String message = "Configuration validation failed:\n" +
521
String.join("\n", errors);
522
throw new ConfigException.Generic(message);
523
}
524
}
525
```
526
527
## Best Practices
528
529
1. **Catch specific exceptions**: Handle different ConfigException subtypes appropriately
530
2. **Provide meaningful defaults**: Have fallback values for optional configuration
531
3. **Log error details**: Include path, origin, and context information in error messages
532
4. **Validate early**: Check configuration validity at application startup
533
5. **Fail fast for critical errors**: Don't continue with invalid critical configuration
534
6. **Use graceful degradation**: Provide reduced functionality when possible
535
7. **Report configuration errors clearly**: Help users understand and fix configuration issues
536
8. **Test error scenarios**: Verify that your error handling works correctly