Skip to content

Error Recovery Module

Module: 04_12_error_recovery Purpose: Comprehensive error handling and recovery strategies for production-grade audio software Status: ✅ Complete

Overview

Professional audio plugins don't crash the DAW when things go wrong. They degrade gracefully, recover automatically, and provide useful diagnostics. This module provides the infrastructure for robust error handling and automatic recovery.

Module Structure

04_12_error_recovery/
├── 04_12_00_error_handling/         # Error definitions and handling
│   ├── error_codes.hpp              # Error taxonomy (40+ predefined codes)
│   ├── result_type.hpp              # Rust-style Result<T,E> monad
│   ├── error_context.hpp            # Rich error context
│   └── error_handler.hpp            # Central error handler
├── 04_12_01_recovery_strategies/    # Recovery mechanisms
│   ├── fallback_processor.hpp       # Safe fallback processing
│   ├── circuit_breaker.hpp          # Automatic feature disabling
│   └── retry_policy.hpp             # Retry with exponential backoff
└── README.md                        # This file

Key Features

1. Error Code Taxonomy

Comprehensive error classification with categories and severity levels:

using namespace audiolab::core::error;

// Categories: Audio, System, User, Internal, Resource, State, Timing
// Severity: Info, Warning, Error, Fatal

ErrorCode err = ErrorCodes::AUDIO_BUFFER_MISMATCH;
if (err.is_fatal()) {
    // Critical error handling
}

40+ Predefined Error Codes: - Audio errors (1000-1999): Buffer mismatch, device lost, sample rate issues - System errors (2000-2999): Out of memory, driver errors - Resource errors (3000-3999): Files not found, load failures - User errors (4000-4999): Invalid parameters, configuration issues - State errors (5000-5999): Invalid transitions, corruption - Internal errors (6000-6999): Assertions, null pointers - Timing errors (7000-7999): RT deadline missed, latency exceeded

2. Result Type (Exception-Free Error Propagation)

Rust-style Result monad for error handling without exceptions:

Result<float, ErrorCode> divide(float a, float b) {
    if (b == 0.0f) {
        return Result<float, ErrorCode>::err(ErrorCodes::DIVISION_BY_ZERO);
    }
    return Result<float, ErrorCode>::ok(a / b);
}

// Usage with chaining
auto result = divide(10.0f, 2.0f)
    .map([](float x) { return x * 2; })          // Transform on success
    .value_or(0.0f);                             // Provide default on error

Monadic Operations: - map() - Transform success value - and_then() - Chain operations returning Result - or_else() - Provide alternative on error - value_or() - Get value with fallback

3. Error Handler (Central Hub)

Centralized error collection and routing with RT-safe deferred reporting:

// Register custom handler
ErrorHandler::instance().register_handler(
    ErrorCategory::Audio,
    [](const ErrorContext& ctx) {
        log_to_file(ctx.format());
    }
);

// Report error (NOT RT-safe)
AUDIOLAB_REPORT_ERROR(ErrorCodes::BUFFER_OVERFLOW, "Size: 1024");

// Report from RT thread (RT-SAFE)
AUDIOLAB_REPORT_ERROR_RT(ErrorCodes::RT_DEADLINE_MISSED);

// Process deferred errors (call from GUI thread)
ErrorHandler::instance().process_deferred_errors();

Features: - Custom handler registration per category - RT-safe deferred error queue (1024 errors) - Error statistics and counting - Automatic overflow detection

4. Fallback Processor (Graceful Degradation)

Safe fallback processing when primary algorithm fails:

FallbackProcessor fallback;

fallback.process_with_fallback(inputs, outputs, numSamples, numChannels,
    [&]() {
        // Try primary processing
        return reverb.process(inputs, outputs, numSamples, numChannels);
    }
);

// Fallback levels: None → SafeDefault → Bypass → Silence

Fallback Hierarchy: 1. None - Normal processing 2. SafeDefault - Use safe defaults (e.g., dry signal) 3. Bypass - Pass input to output (unity gain) 4. Silence - Output silence (last resort)

5. Circuit Breaker (Automatic Disable)

Prevents cascading failures by disabling repeatedly failing components:

CircuitBreaker reverb_breaker(5, 5000);  // 5 failures, 5s cooldown

if (reverb_breaker.allow_operation()) {
    bool success = process_reverb();
    reverb_breaker.record_result(success);
} else {
    // Circuit open - reverb temporarily disabled
    bypass_reverb();
}

// Check recovery periodically
reverb_breaker.check_recovery();

Circuit States: - Closed - Normal operation (requests allowed) - Open - Failures detected (requests blocked) - HalfOpen - Testing recovery (limited requests)

State Transitions: - Closed → Open: After N failures - Open → HalfOpen: After cooldown period - HalfOpen → Closed: On successful operation - HalfOpen → Open: On failed operation

6. Retry Policy (Exponential Backoff)

Automatic retry for transient failures:

RetryPolicy policy(3, 100, 2.0f);  // 3 attempts, 100ms initial, 2x backoff

auto result = policy.execute([&]() -> Result<Data, ErrorCode> {
    return load_resource_from_disk();
});

if (result.is_ok()) {
    use_resource(result.value());
} else {
    handle_persistent_failure(result.error());
}

Features: - Configurable retry attempts - Exponential backoff: delay *= multiplier - Maximum delay cap - Conditional retry (custom predicates)

Usage Examples

Example 1: Safe Resource Loading

Result<AudioFile, ErrorCode> load_audio_file(const std::string& path) {
    RetryPolicy retry(3, 100);

    return retry.execute([&]() -> Result<AudioFile, ErrorCode> {
        std::ifstream file(path);
        if (!file.is_open()) {
            return Result<AudioFile, ErrorCode>::err(
                ErrorCodes::RESOURCE_NOT_FOUND
            );
        }

        AudioFile audio;
        if (!audio.load(file)) {
            return Result<AudioFile, ErrorCode>::err(
                ErrorCodes::RESOURCE_LOAD_FAILED
            );
        }

        return Result<AudioFile, ErrorCode>::ok(std::move(audio));
    });
}

Example 2: RT-Safe Processing with Fallback

class SafeProcessor {
    FallbackProcessor fallback_;
    CircuitBreaker circuit_{5, 5000};

public:
    void process(const float** inputs, float** outputs,
                 uint32_t numChannels, uint32_t numSamples) noexcept {

        fallback_.process_with_fallback(inputs, outputs, numSamples, numChannels,
            [&]() {
                if (!circuit_.allow_operation()) {
                    return false;  // Circuit open
                }

                bool success = try_complex_processing();
                circuit_.record_result(success);
                return success;
            }
        );
    }
};

Example 3: Error Reporting with Context

void validate_buffer(const AudioBuffer& buf) {
    if (buf.num_channels() == 0) {
        AUDIOLAB_REPORT_ERROR(
            ErrorCodes::INVALID_CHANNEL_COUNT,
            "Buffer has zero channels"
        );
        return;
    }

    if (buf.num_samples() > MAX_BUFFER_SIZE) {
        AUDIOLAB_REPORT_ERROR(
            ErrorCodes::AUDIO_BUFFER_MISMATCH,
            "Buffer size " + std::to_string(buf.num_samples()) +
            " exceeds maximum " + std::to_string(MAX_BUFFER_SIZE)
        );
        return;
    }
}

RT-Safety Considerations

RT-Safe Components ✅

  • Result<T,E> - No allocations
  • AUDIOLAB_REPORT_ERROR_RT() - Lock-free queue
  • CircuitBreaker - Atomic operations only
  • FallbackProcessor - No allocations

NOT RT-Safe ⚠️

  • AUDIOLAB_REPORT_ERROR() - Uses strings, locks
  • ErrorHandler::report() - Allocates, locks
  • RetryPolicy - Uses std::this_thread::sleep_for()
  • ErrorHandler::register_handler() - Uses std::map

Rule: Use _RT variants in audio thread, process deferred errors on GUI thread.

Error Recovery Patterns

Pattern 1: Retry-Then-Fallback

auto result = retry_policy.execute([&]() {
    return load_convolution_ir();
});

if (result.is_err()) {
    fallback_processor.enter_fallback(FallbackLevel::SafeDefault);
    use_algorithmic_reverb();
}

Pattern 2: Circuit Breaker + Fallback

if (circuit.allow_operation()) {
    bool success = expensive_operation();
    circuit.record_result(success);

    if (!success) {
        fallback_processor.enter_fallback(FallbackLevel::Bypass);
    }
}

Pattern 3: Conditional Retry

auto result = retry_policy.execute_if(
    [&]() { return network_request(); },
    [](const ErrorCode& err) {
        return err == ErrorCodes::TIMEOUT ||
               err == ErrorCodes::RESOURCE_EXHAUSTED;
    }
);

Best Practices

  1. Always check Result before accessing value

    auto result = risky_operation();
    if (result.is_ok()) {
        use(result.value());
    } else {
        handle_error(result.error());
    }
    

  2. Use RT-safe reporting in audio thread

    // Audio thread
    AUDIOLAB_REPORT_ERROR_RT(ErrorCodes::RT_DEADLINE_MISSED);
    
    // GUI thread (periodically)
    ErrorHandler::instance().process_deferred_errors();
    

  3. Register error handlers during initialization

    // Not thread-safe with report(), so register early
    ErrorHandler::instance().register_handler(
        ErrorCategory::Audio,
        my_audio_error_handler
    );
    

  4. Use circuit breakers for external dependencies

    CircuitBreaker network_breaker(3, 10000);
    CircuitBreaker file_io_breaker(5, 5000);
    

  5. Provide user-friendly error messages

    AUDIOLAB_REPORT_ERROR(
        ErrorCodes::PRESET_LOAD_FAILED,
        "Failed to load '" + preset_name + "': " + specific_reason
    );
    

Integration

Required Headers

#include "04_12_error_recovery/04_12_00_error_handling/error_codes.hpp"
#include "04_12_error_recovery/04_12_00_error_handling/result_type.hpp"
#include "04_12_error_recovery/04_12_00_error_handling/error_handler.hpp"
#include "04_12_error_recovery/04_12_01_recovery_strategies/fallback_processor.hpp"
#include "04_12_error_recovery/04_12_01_recovery_strategies/circuit_breaker.hpp"
#include "04_12_error_recovery/04_12_01_recovery_strategies/retry_policy.hpp"

Namespace

using namespace audiolab::core::error;

Error Code Reference

See error_codes.hpp for complete list of 40+ predefined error codes.

Quick Reference: - SUCCESS (0) - Operation succeeded - AUDIO_BUFFER_MISMATCH (1001) - Buffer size mismatch - OUT_OF_MEMORY (2001) - Memory allocation failed - RESOURCE_NOT_FOUND (3001) - Resource not found - INVALID_PARAMETER (4001) - Parameter out of range - STATE_CORRUPTION (5002) - Plugin state corrupted - DIVISION_BY_ZERO (6003) - Division by zero - RT_DEADLINE_MISSED (7001) - RT deadline missed - MAX_RETRIES_EXCEEDED (8001) - All retry attempts failed

See Also