Error Handling in Apple/Container: A Swift-Native Architecture Guide

Apple/Container implements a type-safe error handling strategy using Swift's Error protocol with domain-specific enums conforming to CustomStringConvertible, enabling clear diagnostics and compile-time safety across modules like ProgressConfig, PluginsService, and the DNS subsystem.

Error handling in apple/container follows Swift idiomatic patterns that prioritize type safety and clear diagnostics. The repository defines domain-specific error enums for each subsystem—from progress rendering to DNS resolution—ensuring that failures are captured at compile time and surfaced with human-readable descriptions. This architecture makes debugging container runtime issues predictable across the codebase.

Domain-Specific Error Types

Apple/Container isolates failure domains by defining typed enums for each component. This approach prevents error collisions and makes failure modes self-documenting.

ProgressConfig.Error in ProgressConfig.swift

The progress rendering system defines its own error type for validation failures. In ProgressConfig.swift, the enum conforms to both Swift.Error and CustomStringConvertible:

public enum Error: Swift.Error, CustomStringConvertible { 
    case invalid(String) 
}

This error is thrown during initialization when parameters fail validation. For example, if totalTasks is zero or negative, the init method throws ProgressConfig.Error.invalid with a descriptive message. See the validation logic in lines 21-34 of ProgressConfig.swift.

PluginsService.Error in PluginsService.swift

The plugin management layer defines PluginsService.Error to handle lookup and loading failures:

public enum Error: Swift.Error, CustomStringConvertible { 
    case pluginNotFound(String)
    case pluginNotLoaded(String) 
}

When PluginsService.load(name:) cannot locate a plugin binary or fails to load it, it propagates these specific cases. The service translates internal failures into descriptive strings like "plugin not found: (name)" at lines 99-108 of PluginsService.swift.

DNS Subsystem Errors

The DNS server uses separate error types for resolution and binding operations. DNSResolverError (defined in Types.swift) and DNSBindError (defined in DNSBindError.swift) both conform to CustomStringConvertible and carry descriptions that mirror DNS RFC error codes. These enums capture specific failure modes like invalid queries or record binding failures.

ContainerizationError in RuntimeService.swift

For container runtime operations, the codebase imports ContainerizationError from the external Containerization package. This error type provides structured failure information including a code (e.g., .invalidArgument, .notFound) and a detailed message. RuntimeService.swift uses this extensively at lines 149, 201, and 390 to flag invalid states, missing resources, or argument errors when manipulating container runtime state.

Supporting Error Types

Additional domain-specific errors include:

  • CommandError (defined in CommandResult.swift): A test-only enum capturing subprocess failures with cases like executionFailed(String) and nonZeroExit(Int, String).
  • FileLogHandler.FileLogFailure (defined in FileLogHandler.swift): Handles logging subsystem failures with cases for openFailed and writeFailed.

Common Error Handling Patterns

Apple/Container follows consistent patterns that make error handling predictable and maintainable.

Typed enums per domain. Each logical area owns its error type, preventing cross-contamination between unrelated subsystems.

CustomStringConvertible conformance. Most errors implement a description property providing concise diagnostics without requiring separate localization tables.

Guard-based validation. Constructors validate arguments early and throw domain-specific errors immediately when preconditions fail. This fail-fast approach appears in ProgressConfig.init and similar validation-heavy initializers.

Error propagation. Higher-level APIs like PluginsService and RuntimeService catch lower-level errors, wrap them if necessary, and re-throw more user-focused enums that preserve context while simplifying caller handling.

Testing with #expect(throws:). The test suite verifies that public APIs throw expected error types, ensuring contracts remain stable across refactors.

Practical Code Examples

Validating ProgressConfig Initialization

When creating a ProgressConfig with invalid parameters, catch the specific error type:

do {
    let cfg = try ProgressConfig(totalTasks: 0)   // invalid: tasks must be > 0
    // use cfg …
} catch let err as ProgressConfig.Error {
    print("Progress config failed:", err)          // "failed to validate config (totalTasks must be greater than zero)"
}

Source: ProgressConfig.swift (lines 21-34).

Handling Plugin Loading Failures

Use pattern matching to distinguish between missing plugins and load failures:

let service = PluginsService(pluginLoader: loader, log: logger)

do {
    try service.load(name: "my-plugin")
} catch PluginsService.Error.pluginNotFound(let name) {
    print("❌ No such plugin:", name)
} catch {
    print("⚠️ Unexpected error:", error)
}

Source: PluginsService.swift (error enum at lines 99-108).

Catching DNS Resolution Errors

The DNS resolver exposes specific error cases for invalid queries:

do {
    let answer = try dnsResolver.resolve("example.invalid")
} catch DNSResolverError.invalidQuery(let reason) {
    print("Bad DNS query:", reason)
}

Source: Types.swift (DNSResolverError definition).

Handling Container Runtime Failures

Runtime operations use the external ContainerizationError type:

do {
    try runtime.start(containerID: "abc123")
} catch let err as ContainerizationError {
    print("Container start failed – code:", err.code, "msg:", err.message)
}

Source: RuntimeService.swift (extensive usage at lines 149, 201, 390).

Key Implementation Files

File Error Type Purpose
ProgressConfig.swift ProgressConfig.Error Validation of progress rendering configuration
PluginsService.swift PluginsService.Error Plugin lookup and loading failures
Types.swift DNSResolverError DNS query resolution errors
DNSBindError.swift DNSBindError DNS record binding failures
RuntimeService.swift ContainerizationError Container runtime state manipulation
CommandResult.swift CommandError Test harness subprocess execution
FileLogHandler.swift FileLogHandler.FileLogFailure Log file I/O operations

Summary

  • Domain isolation: Each subsystem defines its own error enum (e.g., ProgressConfig.Error, PluginsService.Error) preventing type collisions.
  • Human-readable diagnostics: Errors conform to CustomStringConvertible to provide immediate context without localization overhead.
  • Fail-fast validation: Initializers use guard-based validation to throw errors immediately when preconditions fail.
  • Type-safe propagation: Higher-level services catch and re-throw errors with appropriate context, maintaining Swift's type safety throughout the call stack.
  • Comprehensive test coverage: The suite uses #expect(throws:) to verify error contracts remain stable.

Frequently Asked Questions

How does apple/container handle validation errors in configuration objects?

Configuration objects like ProgressConfig validate parameters during initialization and throw domain-specific errors immediately. For example, ProgressConfig.init throws ProgressConfig.Error.invalid when totalTasks is zero or negative, catching invalid states before they propagate to rendering logic.

What distinguishes ContainerizationError from other error types in the codebase?

ContainerizationError is an external dependency from the Containerization package used primarily in RuntimeService.swift, while domain-specific errors like ProgressConfig.Error are defined internally. External errors provide structured codes (e.g., .invalidArgument, .notFound) and messages, whereas internal errors focus on compile-time type safety within specific modules.

How does the DNS subsystem report resolution failures?

The DNS subsystem uses DNSResolverError (defined in Types.swift) and DNSBindError (defined in DNSBindError.swift) to distinguish between query validation failures and record binding issues. Both conform to CustomStringConvertible and include descriptions that align with DNS RFC error codes, making network-level failures diagnosable without digging through implementation details.

What error handling pattern does apple/container use for plugin management?

PluginsService defines PluginsService.Error with cases for pluginNotFound and pluginNotLoaded. When load(name:) fails, it throws these specific errors rather than generic failures, allowing callers to distinguish between missing binaries and load-time crashes using Swift pattern matching in catch blocks.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →