error System

#include <keychain/error.hpp>

Namespace: keychain::error (alias: kc::error)

Overview

The error system in {keychain-core} provides comprehensive error handling using the standard library’s std::error_code system. It defines specific error conditions for all library operations and integrates seamlessly with both exception-based and error code-based error handling patterns.

The system uses strongly-typed error codes that can be compared, categorized, and converted to human-readable messages, enabling robust error handling in cryptographic identity management applications.

Since: v3.0

Error Code Type

kc::code

typedef std::error_code code;

The primary error code type used throughout the keychain library. This is an alias for std::error_code specialized with keychain-specific error categories.

Error Code Enumeration

error_code_t

namespace kc::error {
    enum class error_code_t {
        success = 0,
        deprecated,
        unknown,
        file_system,
        not_implemented,
        persona_exists,
        persona_not_found,
        contact_exists,
        contact_not_found,
        payload_too_large,
        serialization,
        deserialization,
        encryption,
        decryption,
        signature,
        verification,
        consensus,
        certificate_expired,
        certificate_not_yet_valid,
        invalid_algorithm,
        unsupported_format,
        database_error,
        network_error,
        timeout,
        access_denied,
        resource_exhausted,
        // ... and more
    };
}

Common Error Codes:

  • success (0) - Operation completed successfully

  • persona_exists - Attempt to create a persona that already exists

  • persona_not_found - Referenced persona doesn’t exist

  • contact_not_found - Referenced contact doesn’t exist

  • serialization / deserialization - Data format errors

  • encryption / decryption - Cryptographic operation failures

  • signature / verification - Digital signature errors

  • consensus - Consensus protocol errors

  • certificate_expired - PKI certificate validity errors

Console Return Codes

For command-line applications, the error system provides standard exit codes:

namespace kc::error {
    constexpr int failure = -1;
    constexpr int okay = 0;
    constexpr int invalid = 1;
}

Error Categories

The error system uses std::error_category to group related errors:

const std::error_category& get_error_category() noexcept;

This allows applications to check if an error belongs to the keychain library:

if (error_code.category() == kc::error::get_error_category()) {
    // Handle keychain-specific error
}

Usage Examples

Basic Error Checking

#include <keychain/keychain_headers.hpp>

// Function that returns error codes instead of throwing
kc::code create_persona_safe(
    kc::gateway& gateway,
    const std::string& name,
    const std::string& subname,
    kc::persona& out_persona
) {
    try {
        out_persona = gateway.create_persona(name, subname, kc::security_level::medium);
        return kc::error::success;

    } catch (const kc::exception& e) {
        return e.code();
    }
}

int main() {
    kc::gateway gateway(settings);
    kc::persona persona;

    kc::code result = create_persona_safe(gateway, "alice", "personal", persona);

    if (result == kc::error::success) {
        std::cout << "Persona created successfully" << std::endl;
    } else if (result == kc::error::persona_exists) {
        std::cout << "Persona already exists" << std::endl;
    } else {
        std::cout << "Error: " << result.message() << std::endl;
        return kc::error::failure;
    }

    return kc::error::okay;
}

Error Code Comparison

void handle_operation_result(kc::code result) {
    // Direct comparison with error codes
    if (result == kc::error::success) {
        std::cout << "Operation successful" << std::endl;
        return;
    }

    // Check for specific error types
    if (result == kc::error::persona_not_found ||
        result == kc::error::contact_not_found) {
        std::cout << "Identity not found - may need to create" << std::endl;

    } else if (result == kc::error::certificate_expired) {
        std::cout << "Certificate expired - renewal required" << std::endl;

    } else if (result == kc::error::encryption ||
               result == kc::error::decryption) {
        std::cout << "Cryptographic operation failed" << std::endl;

    } else if (result == kc::error::verification) {
        std::cout << "Signature verification failed" << std::endl;

    } else {
        std::cout << "Unexpected error: " << result.message() << std::endl;
    }
}

Error Code Categories

void analyze_error(kc::code error_code) {
    std::cout << "Error: " << error_code.message() << std::endl;
    std::cout << "Code: " << error_code.value() << std::endl;
    std::cout << "Category: " << error_code.category().name() << std::endl;

    // Check if this is a keychain error
    if (error_code.category() == kc::error::get_error_category()) {
        std::cout << "This is a keychain-specific error" << std::endl;

        // Cast to specific error type
        auto kc_error = static_cast<kc::error::error_code_t>(error_code.value());
        switch (kc_error) {
            case kc::error::error_code_t::persona_exists:
                std::cout << "Specific handling for persona_exists" << std::endl;
                break;
            case kc::error::error_code_t::verification:
                std::cout << "Specific handling for verification failure" << std::endl;
                break;
            default:
                std::cout << "General keychain error handling" << std::endl;
                break;
        }
    }
}

Creating Error Codes

// Create error codes from enum values
kc::code make_error_code(kc::error::error_code_t e) noexcept {
    return kc::code(static_cast<int>(e), kc::error::get_error_category());
}

// Function that returns specific error codes
kc::code validate_persona_name(const std::string& name) {
    if (name.empty()) {
        return make_error_code(kc::error::error_code_t::invalid_input);
    }

    if (name.length() > 255) {
        return make_error_code(kc::error::error_code_t::payload_too_large);
    }

    // Check for invalid characters
    if (name.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-") != std::string::npos) {
        return make_error_code(kc::error::error_code_t::invalid_input);
    }

    return kc::error::success;
}

Exception and Error Code Integration

// Convert exceptions to error codes
kc::code safe_encrypt(
    kc::gateway& gateway,
    const kc::persona& persona,
    const kc::serialized_data& data,
    const std::vector<kc::contact>& recipients,
    kc::encrypted_data& out_result
) {
    try {
        out_result = gateway.encrypt(persona, data, recipients);
        return kc::error::success;

    } catch (const kc::invalid_input& e) {
        return e.code();
    } catch (const kc::internal_exception& e) {
        return e.code();
    } catch (const kc::exception& e) {
        return e.code();
    } catch (const std::exception&) {
        return make_error_code(kc::error::error_code_t::unknown);
    }
}

// Convert error codes to exceptions
void throw_on_error(kc::code result) {
    if (result == kc::error::success) {
        return;
    }

    auto error_value = static_cast<kc::error::error_code_t>(result.value());

    switch (error_value) {
        case kc::error::error_code_t::invalid_input:
        case kc::error::error_code_t::payload_too_large:
            throw kc::invalid_input(result);

        case kc::error::error_code_t::certificate_expired:
        case kc::error::error_code_t::timeout:
            throw kc::temporal_exception(result);

        case kc::error::error_code_t::database_error:
        case kc::error::error_code_t::resource_exhausted:
            throw kc::internal_exception(result);

        default:
            throw kc::exception(result);
    }
}

Error Logging and Monitoring

#include <sstream>
#include <chrono>

class ErrorLogger {
public:
    static void log_error(kc::code error_code, const std::string& context = "") {
        auto now = std::chrono::system_clock::now();
        auto time_t = std::chrono::system_clock::to_time_t(now);

        std::ostringstream log_entry;
        log_entry << "[" << std::put_time(std::localtime(&time_t), "%Y-%m-%d %H:%M:%S") << "] ";
        log_entry << "ERROR: " << error_code.message();
        log_entry << " (code: " << error_code.value() << ")";

        if (!context.empty()) {
            log_entry << " Context: " << context;
        }

        // Log to file, syslog, or monitoring system
        std::cerr << log_entry.str() << std::endl;

        // Send to monitoring system for specific error types
        if (error_code == kc::error::verification ||
            error_code == kc::error::certificate_expired) {
            send_to_monitoring_system(error_code, context);
        }
    }

private:
    static void send_to_monitoring_system(kc::code error_code, const std::string& context) {
        // Implementation for monitoring system integration
    }
};

// Usage in application code
kc::code result = gateway_operation();
if (result != kc::error::success) {
    ErrorLogger::log_error(result, "Gateway persona creation");
    handle_operation_result(result);
}

Custom Error Conditions

// Application-specific error handling
bool is_recoverable_error(kc::code error_code) {
    return error_code == kc::error::timeout ||
           error_code == kc::error::network_error ||
           error_code == kc::error::resource_exhausted;
}

bool is_user_error(kc::code error_code) {
    return error_code == kc::error::invalid_input ||
           error_code == kc::error::persona_not_found ||
           error_code == kc::error::contact_not_found;
}

bool is_security_error(kc::code error_code) {
    return error_code == kc::error::verification ||
           error_code == kc::error::signature ||
           error_code == kc::error::access_denied ||
           error_code == kc::error::certificate_expired;
}

void handle_error_by_category(kc::code error_code) {
    if (is_user_error(error_code)) {
        std::cout << "User action required: " << error_code.message() << std::endl;
    } else if (is_security_error(error_code)) {
        std::cout << "Security error: " << error_code.message() << std::endl;
        // Additional security logging
    } else if (is_recoverable_error(error_code)) {
        std::cout << "Temporary error: " << error_code.message() << std::endl;
        // Retry logic
    } else {
        std::cout << "System error: " << error_code.message() << std::endl;
        // System-level error handling
    }
}

Best Practices

Error Code Checking

  • Always check return values for functions that return kc::code

  • Use specific error code comparisons rather than just checking for success/failure

  • Provide meaningful error messages to users based on specific error codes

Exception vs Error Code Patterns

  • Use exceptions for exceptional conditions that should interrupt normal flow

  • Use error codes for expected failure conditions that can be handled programmatically

  • Convert between exceptions and error codes at API boundaries as needed

Error Propagation

  • Preserve original error codes when propagating errors through multiple layers

  • Add context information when converting between error handling mechanisms

  • Log errors at appropriate levels (debug, info, warning, error)

Integration with TKCrypt

The keychain error system integrates with the underlying TKCrypt library error system (tkc::error). TKCrypt errors are typically converted to keychain errors at the API boundary, providing a unified error interface for applications.

See Also