CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-typesafe--config

Configuration library for JVM languages using HOCON files

Pending
Overview
Eval results
Files

exceptions.mddocs/

Exception Handling

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.

Exception Hierarchy

ConfigException Base Class

Base class for all configuration-related exceptions.

public abstract class ConfigException extends RuntimeException {
    public ConfigException(String message);
    public ConfigException(String message, Throwable cause);
    public ConfigOrigin origin();
}

Common Methods:

  • getMessage() - Human-readable error description
  • getCause() - Underlying exception if applicable
  • origin() - Configuration origin where error occurred

Configuration Access Exceptions

ConfigException.Missing

Thrown when a required configuration path is not found.

public static class Missing extends ConfigException {
    public String path();
}

Usage Examples:

try {
    String dbUrl = config.getString("database.url");
} catch (ConfigException.Missing e) {
    String missingPath = e.path();
    System.err.println("Missing required configuration: " + missingPath);
    
    // Provide default or fail gracefully
    String defaultUrl = "jdbc:h2:mem:default";
    System.out.println("Using default database URL: " + defaultUrl);
}

ConfigException.Null

Thrown when a configuration path exists but has a null value.

public static class Null extends ConfigException.Missing {
    // Inherits path() method from Missing
}

Usage Examples:

try {
    String apiKey = config.getString("api.key");
} catch (ConfigException.Null e) {
    System.err.println("API key is explicitly set to null at: " + e.path());
    // Handle null value scenario
} catch (ConfigException.Missing e) {
    System.err.println("API key configuration is missing: " + e.path());
    // Handle missing value scenario
}

ConfigException.WrongType

Thrown when requesting a value with an incompatible type.

public static class WrongType extends ConfigException {
    public String path();
    public ConfigValueType expected();
    public ConfigValueType actual();
}

Usage Examples:

try {
    int port = config.getInt("server.port");
} catch (ConfigException.WrongType e) {
    String path = e.path();
    ConfigValueType expected = e.expected();
    ConfigValueType actual = e.actual();
    
    System.err.println(String.format(
        "Type mismatch at %s: expected %s but got %s",
        path, expected, actual
    ));
    
    // Try alternative approaches
    if (actual == ConfigValueType.STRING) {
        try {
            String portStr = config.getString(path);
            int port = Integer.parseInt(portStr);
            // Use parsed value
        } catch (NumberFormatException nfe) {
            // Handle invalid string format
        }
    }
}

Parsing Exceptions

ConfigException.Parse

Thrown when configuration files contain syntax errors.

public static class Parse extends ConfigException {
    public ConfigOrigin origin();
}

Usage Examples:

try {
    Config config = ConfigFactory.parseFile(configFile);
} catch (ConfigException.Parse e) {
    ConfigOrigin origin = e.origin();
    System.err.println(String.format(
        "Parse error in %s at line %d: %s",
        origin.filename(),
        origin.lineNumber(),
        e.getMessage()
    ));
    
    // Handle parse error - maybe use defaults
    Config defaultConfig = ConfigFactory.parseResources("reference.conf");
}

ConfigException.IO

Thrown for I/O related errors when loading configuration files.

public static class IO extends ConfigException {
    public ConfigOrigin origin();
}

Usage Examples:

try {
    Config config = ConfigFactory.parseFile(new File("/etc/myapp/config.conf"));
} catch (ConfigException.IO e) {
    System.err.println("I/O error reading configuration: " + e.getMessage());
    
    // Try alternative locations
    try {
        config = ConfigFactory.parseFile(new File("./config.conf"));
    } catch (ConfigException.IO e2) {
        // Fall back to classpath resource
        config = ConfigFactory.parseResources("application.conf");
    }
}

Resolution Exceptions

ConfigException.UnresolvedSubstitution

Thrown when substitutions cannot be resolved.

public static class UnresolvedSubstitution extends ConfigException {
    public String path();
    public String detail();
}

Usage Examples:

try {
    Config resolved = config.resolve();
} catch (ConfigException.UnresolvedSubstitution e) {
    String path = e.path();
    String detail = e.detail();
    
    System.err.println(String.format(
        "Cannot resolve substitution ${%s}: %s", path, detail
    ));
    
    // Option 1: Allow partial resolution
    Config partiallyResolved = config.resolve(
        ConfigResolveOptions.defaults().setAllowUnresolved(true)
    );
    
    // Option 2: Provide missing values
    Config withDefaults = config.withFallback(
        ConfigFactory.parseString(path + " = \"default-value\"")
    ).resolve();
}

ConfigException.NotResolved

Thrown when trying to access values from an unresolved configuration.

public static class NotResolved extends ConfigException {
    // Standard exception methods
}

Usage Examples:

Config config = ConfigFactory.parseString("value = ${missing.ref}");

try {
    // This will fail because config is not resolved
    String value = config.getString("value");
} catch (ConfigException.NotResolved e) {
    System.err.println("Configuration must be resolved first: " + e.getMessage());
    
    // Resolve first
    try {
        Config resolved = config.resolve();
        String value = resolved.getString("value");
    } catch (ConfigException.UnresolvedSubstitution ue) {
        // Handle unresolved substitutions
    }
}

Validation Exceptions

ConfigException.ValidationFailed

Thrown by the checkValid() method when validation fails.

public static class ValidationFailed extends ConfigException {
    public Iterable<ValidationProblem> problems();
    
    public static class ValidationProblem {
        public String path();
        public String problem();
        public ConfigOrigin origin();
    }
}

Usage Examples:

Config reference = ConfigFactory.parseResources("reference.conf");
Config config = ConfigFactory.load();

try {
    config.checkValid(reference);
} catch (ConfigException.ValidationFailed e) {
    System.err.println("Configuration validation failed:");
    
    for (ConfigException.ValidationProblem problem : e.problems()) {
        String path = problem.path();
        String description = problem.problem();
        ConfigOrigin origin = problem.origin();
        
        System.err.println(String.format(
            "  %s: %s (from %s:%d)",
            path, description,
            origin.filename(), origin.lineNumber()
        ));
    }
    
    // Handle validation failures
    System.exit(1);
}

Value-Specific Exceptions

ConfigException.BadValue

Thrown when a configuration value is invalid for its intended use.

public static class BadValue extends ConfigException {
    public String path();
    public String detail();
}

Usage Examples:

try {
    Duration timeout = config.getDuration("timeout");
} catch (ConfigException.BadValue e) {
    System.err.println(String.format(
        "Invalid duration value at %s: %s",
        e.path(), e.detail()
    ));
    
    // Use default duration
    Duration defaultTimeout = Duration.ofSeconds(30);
}

try {
    ConfigMemorySize heapSize = config.getMemorySize("jvm.heap");
} catch (ConfigException.BadValue e) {
    System.err.println(String.format(
        "Invalid memory size at %s: %s",
        e.path(), e.detail()
    ));
}

ConfigException.BadPath

Thrown when a path expression is malformed.

public static class BadPath extends ConfigException {
    public String path();
    public String detail();
}

Usage Examples:

try {
    // Invalid path with special characters
    boolean value = config.getBoolean("invalid..path");
} catch (ConfigException.BadPath e) {
    System.err.println(String.format(
        "Invalid path expression '%s': %s",
        e.path(), e.detail()
    ));
    
    // Use quoted path or fix the path
    String quotedPath = ConfigUtil.joinPath("invalid", "", "path");
    boolean value = config.getBoolean(quotedPath);
}

JavaBean Exceptions

ConfigException.BadBean

Thrown by ConfigBeanFactory when JavaBean creation fails.

public static class BadBean extends ConfigException {
    // Standard exception methods
}

Usage Examples:

public class DatabaseConfig {
    private String host;
    private int port;
    // getters and setters...
}

try {
    DatabaseConfig dbConfig = ConfigBeanFactory.create(
        config.getConfig("database"), 
        DatabaseConfig.class
    );
} catch (ConfigException.BadBean e) {
    System.err.println("Failed to create DatabaseConfig bean: " + e.getMessage());
    
    // Manual configuration extraction
    DatabaseConfig dbConfig = new DatabaseConfig();
    dbConfig.setHost(config.getString("database.host"));
    dbConfig.setPort(config.getInt("database.port"));
}

Internal Exceptions

ConfigException.BugOrBroken

Thrown for internal library errors that indicate bugs.

public static class BugOrBroken extends ConfigException {
    // Standard exception methods
}

Usage: This exception indicates a bug in the Config library itself. If encountered, it should be reported to the library maintainers.

ConfigException.Generic

Generic exception for miscellaneous error conditions.

public static class Generic extends ConfigException {
    // Standard exception methods
}

Exception Handling Patterns

Graceful Degradation

Handle configuration errors while maintaining application functionality.

public class ConfigService {
    private final Config config;
    
    public ConfigService() {
        Config primaryConfig = null;
        
        try {
            // Try primary configuration source
            primaryConfig = ConfigFactory.load("application.conf");
        } catch (ConfigException.IO e) {
            System.err.println("Primary config not found, trying backup");
            try {
                primaryConfig = ConfigFactory.load("backup.conf");
            } catch (ConfigException e2) {
                System.err.println("Backup config failed, using defaults");
                primaryConfig = ConfigFactory.parseResources("reference.conf");
            }
        }
        
        this.config = primaryConfig;
    }
    
    public String getStringWithDefault(String path, String defaultValue) {
        try {
            return config.getString(path);
        } catch (ConfigException.Missing | ConfigException.WrongType e) {
            System.err.println("Using default for " + path + ": " + e.getMessage());
            return defaultValue;
        }
    }
}

Error Recovery

Implement recovery strategies for different exception types.

public Config loadConfigWithRecovery(String resourceName) {
    try {
        return ConfigFactory.load(resourceName).resolve();
    } catch (ConfigException.Parse e) {
        // Parse error - try alternative formats
        System.err.println("Parse error, trying JSON format: " + e.getMessage());
        return ConfigFactory.parseResources(resourceName + ".json")
            .resolve();
    } catch (ConfigException.UnresolvedSubstitution e) {
        // Resolution error - provide fallbacks
        System.err.println("Resolution error, using partial config: " + e.getMessage());
        return ConfigFactory.load(resourceName)
            .resolve(ConfigResolveOptions.defaults().setAllowUnresolved(true));
    } catch (ConfigException.IO e) {
        // I/O error - use embedded defaults
        System.err.println("I/O error, using embedded config: " + e.getMessage());
        return ConfigFactory.parseResources("reference.conf").resolve();
    }
}

Validation and Error Reporting

Comprehensive error reporting for configuration issues.

public void validateConfiguration(Config config) throws ConfigException {
    List<String> errors = new ArrayList<>();
    
    // Check required paths
    String[] requiredPaths = {
        "database.url", "database.username", "server.port"
    };
    
    for (String path : requiredPaths) {
        try {
            if (!config.hasPath(path)) {
                errors.add("Missing required configuration: " + path);
            } else if (config.getIsNull(path)) {
                errors.add("Required configuration is null: " + path);
            }
        } catch (ConfigException e) {
            errors.add("Error checking " + path + ": " + e.getMessage());
        }
    }
    
    // Check value types and ranges
    try {
        int port = config.getInt("server.port");
        if (port < 1 || port > 65535) {
            errors.add("Server port must be between 1 and 65535, got: " + port);
        }
    } catch (ConfigException.Missing e) {
        // Already handled above
    } catch (ConfigException.WrongType e) {
        errors.add("Server port must be a number: " + e.getMessage());
    }
    
    if (!errors.isEmpty()) {
        String message = "Configuration validation failed:\n" + 
            String.join("\n", errors);
        throw new ConfigException.Generic(message);
    }
}

Best Practices

  1. Catch specific exceptions: Handle different ConfigException subtypes appropriately
  2. Provide meaningful defaults: Have fallback values for optional configuration
  3. Log error details: Include path, origin, and context information in error messages
  4. Validate early: Check configuration validity at application startup
  5. Fail fast for critical errors: Don't continue with invalid critical configuration
  6. Use graceful degradation: Provide reduced functionality when possible
  7. Report configuration errors clearly: Help users understand and fix configuration issues
  8. Test error scenarios: Verify that your error handling works correctly

Install with Tessl CLI

npx tessl i tessl/maven-com-typesafe--config

docs

access.md

exceptions.md

index.md

loading.md

options.md

resolution.md

values.md

tile.json