How to Handle Errors in CodexBar Scanning: A Type-Driven Approach
CodexBar handles scanning errors through provider-specific enums conforming to LocalizedError, centralized orchestration via ProviderFetchPlan, and observable UI propagation to ensure robust data aggregation across dozens of AI providers.
Handling network failures, authentication issues, and parsing errors is critical when gathering usage data from multiple AI services. In the steipete/CodexBar repository, the scanning pipeline implements a typed-error-per-provider strategy combined with centralized error aggregation to maintain code maintainability and UI stability. This article examines how to handle errors in CodexBar scanning by analyzing the source code architecture from low-level probes to high-level SwiftUI updates.
Provider-Specific Error Enums
Every probe in CodexBar defines a dedicated error enum that conforms to LocalizedError, Sendable, and often Equatable. This typed approach ensures exhaustive error handling and human-readable descriptions without fragile string matching.
For example, the Zed provider defines ZedStatusProbeError in Sources/CodexBarCore/Providers/Zed/ZedStatusProbe.swift:
public enum ZedStatusProbeError: LocalizedError, Sendable, Equatable {
case unauthorized
case notSignedIn
case invalidServerURL
case parseFailed(String)
case apiError(Int, String)
// Additional cases...
}
Similarly, the Windsurf provider implements WindsurfStatusProbeError in Sources/CodexBarCore/Providers/Windsurf/WindsurfStatusProbe.swift, while Claude uses ClaudeUsageError in Sources/CodexBarCore/Providers/Claude/ClaudeUsageFetcher.swift.
These enums categorize failures into distinct types:
- Network errors:
URLError(.cannotConnectToHost),CancellationError - Authentication failures:
unauthorized,notSignedIn,invalidCookie - Parsing issues:
parseFailed(String),apiError(Int, String) - Configuration problems:
invalidEndpointOverride,untrustedServerConfiguration - Quota limitations:
quotaExceeded,usageUnavailable
Because each enum adopts LocalizedError, the errorDescription property provides consistent, user-friendly messaging that the UI can display directly.
Centralized Error Orchestration
While individual probes throw typed errors, the ProviderFetchPlan and ProviderCandidateRetryRunner classes manage centralized error aggregation and retry logic.
ProviderFetchPlan
Located in Sources/CodexBarCore/Providers/ProviderFetchPlan.swift, this class defines the sequence of fetchers for a given provider and wraps individual errors in ProviderFetchError. This thin wrapper also conforms to LocalizedError, preserving human-readable descriptions while unifying the error type for the orchestration layer.
ProviderCandidateRetryRunner
Defined in Sources/CodexBarCore/Providers/ProviderCandidateRetryRunner.swift, this runner executes candidate fetchers with exponential backoff. It distinguishes between recoverable errors (temporary network glitches that trigger retries) and irrecoverable errors (malformed API keys that short-circuit the scan). Irrecoverable failures are immediately surfaced to the UI via Issue.record(_:) entries.
Propagating Errors to the UI
The scanning pipeline returns Result<ProbeData, ProviderFetchError> to the StatusItemController, which bridges errors to the SwiftUI interface.
When a failure occurs, the controller performs three actions:
- Calls
error.localizedDescription(leveragingLocalizedErrorconformance) to obtain a friendly string - Stores the error in the
@ObservablemodelStatusItemState, triggering automatic SwiftUI updates - Optionally activates UI hints, such as flashing warning icons, based on error severity
This architecture enables context-specific UI actions. For instance, an invalidEndpointOverride error can trigger an "Open Settings" button, while network errors might display a "Retry" option.
Testing Error Handling Paths
Every error enum includes comprehensive unit tests to prevent regression. The test suite verifies that the orchestration layer correctly propagates each failure type.
Key test files include:
Tests/CodexBarTests/ZedStatusProbeTests.swift: CoversinvalidServerURL,unauthorized, and cancellation errors using#expect(throws:)syntaxTests/CodexBarTests/WindsurfStatusProbeTests.swift: Tests SQLite-related failures includingopenFailedandprepareFailedTests/CodexBarTests/ProviderCandidateRetryRunnerTests.swift: Validates retry logic with mockTestErrorinstancesTests/CodexBarTests/StatusProbeTests.swift: Verifies complex scenarios such as "browser scan stops importing after later browser succeeds"
These tests ensure that future refactors cannot silently swallow failure cases or break error propagation chains.
Code Examples
Handling Provider-Specific Errors
When invoking a probe directly, catch the specific error type for granular handling:
do {
let result = try await ZedStatusProbe().scan()
// Update UI with successful data
} catch let error as ZedStatusProbeError {
logger.error("Zed probe failed: \(error.localizedDescription)")
statusItem.showError(error) // Updates UI badge
} catch {
logger.error("Unexpected error: \(error)")
statusItem.showGenericError()
}
Using ProviderFetchPlan for Centralized Handling
For coordinated fetching with automatic retry logic:
let plan = ProviderFetchPlan(provider: .zed, candidates: [.statusProbe, .tokenFetcher])
do {
let data = try await plan.run()
statusItem.update(with: data)
} catch let fetchError as ProviderFetchError {
// All provider-specific errors wrapped uniformly
statusItem.showError(fetchError)
}
SwiftUI Error Display
The observable state model enables reactive error presentation:
@Observable final class StatusItemState {
var errorMessage: String? = nil
// ...
}
// In the view:
if let message = state.errorMessage {
Text(message)
.foregroundColor(.red)
.font(.caption)
}
Summary
- Type safety: Every provider defines a specific error enum (e.g.,
ZedStatusProbeError,ClaudeUsageError) conforming toLocalizedErrorfor exhaustive handling and human-readable descriptions. - Centralized orchestration:
ProviderFetchPlanandProviderCandidateRetryRunnermanage retries, distinguish recoverable from irrecoverable errors, and wrap failures inProviderFetchError. - UI integration: The
StatusItemControllerpropagates errors throughStatusItemState, enabling SwiftUI to display context-specific actions and status badges. - Test coverage: Comprehensive suites in
ZedStatusProbeTests.swift,WindsurfStatusProbeTests.swift, andProviderCandidateRetryRunnerTests.swiftverify error propagation using#expect(throws:)assertions.
Frequently Asked Questions
What error types does CodexBar use for provider scanning?
CodexBar uses provider-specific enums such as ZedStatusProbeError, WindsurfStatusProbeError, and ClaudeUsageError. Each enum conforms to LocalizedError, Sendable, and Equatable to provide type-safe error handling and human-readable descriptions for network, authentication, parsing, and configuration failures.
How does CodexBar handle retry logic when a scan fails?
The ProviderCandidateRetryRunner class in Sources/CodexBarCore/Providers/ProviderCandidateRetryRunner.swift implements exponential backoff for recoverable errors like temporary network glitches. Irrecoverable errors such as invalid API keys short-circuit immediately and propagate to the UI without retry attempts.
Can the UI display different actions based on specific error types?
Yes. Because all errors are typed enums conforming to LocalizedError, the StatusItemController can inspect specific cases (e.g., invalidEndpointOverride) and trigger context-appropriate actions like "Open Settings" buttons. This avoids fragile string-matching and ensures type-safe UI logic.
Where are the error handling tests located in the CodexBar repository?
Error handling tests are distributed across several files: Tests/CodexBarTests/ZedStatusProbeTests.swift for Zed-specific errors, Tests/CodexBarTests/WindsurfStatusProbeTests.swift for SQLite-related failures, and Tests/CodexBarTests/ProviderCandidateRetryRunnerTests.swift for retry logic validation. These use Swift Testing's #expect(throws:) syntax to verify correct error propagation.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →