Error Handling Functions

#include <keychain/keychain_headers.h>

Overview

Error handling functions provide comprehensive error code management, message retrieval, and diagnostic information for debugging and troubleshooting Keychain Core C API operations.

Error Code Functions

kc_common_get_error_message()

unsigned int kc_common_get_error_message(
    char** out_error_message,
    unsigned int* out_message_size,
    unsigned int error_code
)

Gets a human-readable error message for a specific error code.

Parameters:

  • out_error_message - Pointer to error message string pointer (output, caller must free)

  • out_message_size - Pointer to message string length (output)

  • error_code - Error code to get message for

Returns: Error code (0 = success)

Example:

unsigned int result = kc_gateway_create_persona(gateway, &persona, "test", 4, "test", 4, KC_SECURITY_LEVEL_HIGH, 1);
if (result != 0) {
    char* error_msg = NULL;
    unsigned int msg_size = 0;

    unsigned int get_result = kc_common_get_error_message(&error_msg, &msg_size, result);
    if (get_result == 0) {
        printf("Operation failed: %s (error code: %u)\n", error_msg, result);
        kc_common_delete_buffer(&error_msg);
    } else {
        printf("Operation failed with error code: %u\n", result);
    }
}

kc_common_get_last_error()

unsigned int kc_common_get_last_error(unsigned int* out_error_code)

Gets the error code from the last failed operation in the current thread.

Parameters:

  • out_error_code - Pointer to error code (output)

Returns: Error code (0 = success, non-zero if no error available)

Example:

// Perform some operation that might fail
unsigned int result = kc_gateway_hash(&hash_result, &hash_size, "data", 4);

// Check if operation failed
if (result != 0) {
    unsigned int last_error = 0;
    unsigned int get_result = kc_common_get_last_error(&last_error);

    if (get_result == 0) {
        printf("Last error code: %u\n", last_error);
        assert(last_error == result); // Should match
    }
}

kc_common_clear_last_error()

unsigned int kc_common_clear_last_error()

Clears the last error for the current thread.

Returns: Error code (0 = success)

Example:

// Clear any previous errors
kc_common_clear_last_error();

// Perform operation
unsigned int result = kc_gateway_some_operation(gateway);

// Check for new errors
unsigned int last_error = 0;
if (kc_common_get_last_error(&last_error) == 0) {
    printf("New error occurred: %u\n", last_error);
}

kc_common_is_error_recoverable()

unsigned int kc_common_is_error_recoverable(
    unsigned int error_code,
    unsigned int* out_is_recoverable
)

Determines if an error condition is recoverable or requires restart.

Parameters:

  • error_code - Error code to check

  • out_is_recoverable - Pointer to recoverable flag (output, 1 = recoverable, 0 = fatal)

Returns: Error code (0 = success)

Example:

unsigned int operation_result = kc_gateway_connect_to_server(gateway);
if (operation_result != 0) {
    unsigned int is_recoverable = 0;
    unsigned int check_result = kc_common_is_error_recoverable(operation_result, &is_recoverable);

    if (check_result == 0) {
        if (is_recoverable) {
            printf("Recoverable error - retrying operation\n");
            // Implement retry logic
        } else {
            printf("Fatal error - application restart required\n");
            exit(1);
        }
    }
}

Error Category Functions

kc_common_get_error_category()

unsigned int kc_common_get_error_category(
    unsigned int error_code,
    char** out_category_name,
    unsigned int* out_name_size
)

Gets the category name for an error code (e.g., "NETWORK", "CRYPTO", "VALIDATION").

Parameters:

  • error_code - Error code to categorize

  • out_category_name - Pointer to category name string pointer (output, caller must free)

  • out_name_size - Pointer to name string length (output)

Returns: Error code (0 = success)

Example:

unsigned int crypto_error = KC_ERROR_INVALID_SIGNATURE;
char* category = NULL;
unsigned int category_size = 0;

unsigned int result = kc_common_get_error_category(crypto_error, &category, &category_size);
if (result == 0) {
    printf("Error category: %s\n", category); // Outputs: "CRYPTO"
    kc_common_delete_buffer(&category);
}

kc_common_get_error_severity()

unsigned int kc_common_get_error_severity(
    unsigned int error_code,
    unsigned int* out_severity_level
)

Gets the severity level for an error code (1=INFO, 2=WARNING, 3=ERROR, 4=CRITICAL).

Parameters:

  • error_code - Error code to check

  • out_severity_level - Pointer to severity level (output)

Returns: Error code (0 = success)

Example:

unsigned int network_error = KC_ERROR_CONNECTION_TIMEOUT;
unsigned int severity = 0;

unsigned int result = kc_common_get_error_severity(network_error, &severity);
if (result == 0) {
    switch (severity) {
        case 1: printf("INFO level error\n"); break;
        case 2: printf("WARNING level error\n"); break;
        case 3: printf("ERROR level error\n"); break;
        case 4: printf("CRITICAL level error\n"); break;
    }
}

Diagnostic Functions

kc_common_get_diagnostic_info()

unsigned int kc_common_get_diagnostic_info(
    char** out_diagnostic_json,
    unsigned int* out_json_size
)

Gets comprehensive diagnostic information about the current library state in JSON format.

Parameters:

  • out_diagnostic_json - Pointer to diagnostic JSON string pointer (output, caller must free)

  • out_json_size - Pointer to JSON string length (output)

Returns: Error code (0 = success)

Example:

char* diagnostics = NULL;
unsigned int diag_size = 0;

unsigned int result = kc_common_get_diagnostic_info(&diagnostics, &diag_size);
if (result == 0) {
    printf("Library Diagnostics:\n%s\n", diagnostics);
    kc_common_delete_buffer(&diagnostics);
}

kc_common_get_call_stack()

unsigned int kc_common_get_call_stack(
    char** out_stack_trace,
    unsigned int* out_trace_size
)

Gets the current call stack for debugging purposes (debug builds only).

Parameters:

  • out_stack_trace - Pointer to stack trace string pointer (output, caller must free)

  • out_trace_size - Pointer to trace string length (output)

Returns: Error code (0 = success)

Example:

#ifdef DEBUG
char* stack_trace = NULL;
unsigned int trace_size = 0;

unsigned int result = kc_common_get_call_stack(&stack_trace, &trace_size);
if (result == 0) {
    printf("Call Stack:\n%s\n", stack_trace);
    kc_common_delete_buffer(&stack_trace);
}
#endif

kc_common_enable_debug_logging()

unsigned int kc_common_enable_debug_logging(
    const char* log_file_path,
    unsigned int path_size,
    unsigned int log_level
)

Enables debug logging to a file with specified level (1=ERROR, 2=WARN, 3=INFO, 4=DEBUG, 5=TRACE).

Parameters:

  • log_file_path - Path to log file

  • path_size - Length of file path

  • log_level - Logging level (1-5)

Returns: Error code (0 = success)

Example:

const char* log_path = "/var/log/keychain_debug.log";
unsigned int result = kc_common_enable_debug_logging(log_path, strlen(log_path), 4); // DEBUG level
if (result == 0) {
    printf("Debug logging enabled at DEBUG level\n");
}

kc_common_disable_debug_logging()

unsigned int kc_common_disable_debug_logging()

Disables debug logging and closes log files.

Returns: Error code (0 = success)

Common Error Codes

The following error codes are commonly returned by Keychain Core functions:

General Errors

  • KC_SUCCESS (0) - Operation completed successfully

  • KC_ERROR_INVALID_PARAMETER (1) - Invalid parameter provided

  • KC_ERROR_NULL_POINTER (2) - Required pointer is NULL

  • KC_ERROR_BUFFER_TOO_SMALL (3) - Provided buffer is too small

  • KC_ERROR_OUT_OF_MEMORY (4) - Memory allocation failed

  • KC_ERROR_NOT_INITIALIZED (5) - Library not properly initialized

Network Errors (100-199)

  • KC_ERROR_CONNECTION_FAILED (100) - Failed to connect to server

  • KC_ERROR_CONNECTION_TIMEOUT (101) - Connection timed out

  • KC_ERROR_NETWORK_UNREACHABLE (102) - Network is unreachable

  • KC_ERROR_SERVER_UNAVAILABLE (103) - Server is unavailable

  • KC_ERROR_PROTOCOL_ERROR (104) - Protocol communication error

Cryptographic Errors (200-299)

  • KC_ERROR_INVALID_SIGNATURE (200) - Digital signature is invalid

  • KC_ERROR_INVALID_CERTIFICATE (201) - Certificate is invalid or expired

  • KC_ERROR_KEY_NOT_FOUND (202) - Cryptographic key not found

  • KC_ERROR_ENCRYPTION_FAILED (203) - Encryption operation failed

  • KC_ERROR_DECRYPTION_FAILED (204) - Decryption operation failed

  • KC_ERROR_HASH_MISMATCH (205) - Hash values do not match

Validation Errors (300-399)

  • KC_ERROR_VALIDATION_FAILED (300) - Data validation failed

  • KC_ERROR_INVALID_FORMAT (301) - Data format is invalid

  • KC_ERROR_SCHEMA_VIOLATION (302) - Data violates schema rules

  • KC_ERROR_CONSTRAINT_VIOLATION (303) - Data violates constraints

Storage Errors (400-499)

  • KC_ERROR_FILE_NOT_FOUND (400) - Required file not found

  • KC_ERROR_FILE_ACCESS_DENIED (401) - File access permission denied

  • KC_ERROR_DISK_FULL (402) - Insufficient disk space

  • KC_ERROR_STORAGE_CORRUPTED (403) - Storage data is corrupted

Usage Patterns

Basic Error Handling Pattern

// Standard error handling pattern for Keychain operations
unsigned int safe_operation_with_error_handling() {
    char* result_buffer = NULL;
    unsigned int buffer_size = 0;

    // Clear any previous errors
    kc_common_clear_last_error();

    // Perform operation
    unsigned int result = kc_gateway_some_operation(gateway, &result_buffer, &buffer_size);

    if (result != 0) {
        // Get detailed error information
        char* error_msg = NULL;
        unsigned int msg_size = 0;
        char* category = NULL;
        unsigned int category_size = 0;
        unsigned int severity = 0;

        kc_common_get_error_message(&error_msg, &msg_size, result);
        kc_common_get_error_category(result, &category, &category_size);
        kc_common_get_error_severity(result, &severity);

        printf("Operation failed:\n");
        printf("  Error code: %u\n", result);
        printf("  Message: %s\n", error_msg ? error_msg : "Unknown error");
        printf("  Category: %s\n", category ? category : "Unknown");
        printf("  Severity: %u\n", severity);

        // Cleanup error strings
        if (error_msg) kc_common_delete_buffer(&error_msg);
        if (category) kc_common_delete_buffer(&category);

        return result;
    }

    // Success - use result_buffer
    if (result_buffer) {
        printf("Operation succeeded: %s\n", result_buffer);
        kc_common_delete_buffer(&result_buffer);
    }

    return KC_SUCCESS;
}

Retry Logic with Error Analysis

// Implement retry logic based on error recoverability
unsigned int operation_with_retry(kc_gateway_t* gateway, int max_retries) {
    int retry_count = 0;
    unsigned int result;

    do {
        result = kc_gateway_connect_to_server(gateway);

        if (result == KC_SUCCESS) {
            printf("Connection successful on attempt %d\n", retry_count + 1);
            return KC_SUCCESS;
        }

        // Check if error is recoverable
        unsigned int is_recoverable = 0;
        unsigned int check_result = kc_common_is_error_recoverable(result, &is_recoverable);

        if (check_result != 0 || !is_recoverable) {
            char* error_msg = NULL;
            unsigned int msg_size = 0;
            kc_common_get_error_message(&error_msg, &msg_size, result);

            printf("Non-recoverable error: %s\n", error_msg ? error_msg : "Unknown");
            if (error_msg) kc_common_delete_buffer(&error_msg);
            return result;
        }

        retry_count++;
        if (retry_count < max_retries) {
            printf("Recoverable error on attempt %d, retrying...\n", retry_count);

            // Exponential backoff
            unsigned int delay_ms = 1000 * (1 << (retry_count - 1)); // 1s, 2s, 4s, 8s...
            #ifdef _WIN32
                Sleep(delay_ms);
            #else
                usleep(delay_ms * 1000);
            #endif
        }

    } while (retry_count < max_retries);

    printf("Operation failed after %d attempts\n", max_retries);
    return result;
}

Comprehensive Error Logging

// Log errors with full context for debugging
void log_error_with_context(unsigned int error_code, const char* operation_name) {
    char* error_msg = NULL;
    char* category = NULL;
    char* diagnostics = NULL;
    unsigned int msg_size = 0;
    unsigned int category_size = 0;
    unsigned int diag_size = 0;
    unsigned int severity = 0;

    // Get all available error information
    kc_common_get_error_message(&error_msg, &msg_size, error_code);
    kc_common_get_error_category(error_code, &category, &category_size);
    kc_common_get_error_severity(error_code, &severity);
    kc_common_get_diagnostic_info(&diagnostics, &diag_size);

    // Log to file or console
    FILE* log_file = fopen("/var/log/keychain_errors.log", "a");
    if (log_file) {
        time_t now = time(NULL);
        char* timestamp = ctime(&now);
        timestamp[strlen(timestamp) - 1] = '\0'; // Remove newline

        fprintf(log_file, "\n=== ERROR LOG ENTRY ===\n");
        fprintf(log_file, "Timestamp: %s\n", timestamp);
        fprintf(log_file, "Operation: %s\n", operation_name);
        fprintf(log_file, "Error Code: %u\n", error_code);
        fprintf(log_file, "Message: %s\n", error_msg ? error_msg : "Unknown");
        fprintf(log_file, "Category: %s\n", category ? category : "Unknown");
        fprintf(log_file, "Severity: %u\n", severity);

        if (diagnostics) {
            fprintf(log_file, "Diagnostics:\n%s\n", diagnostics);
        }

        #ifdef DEBUG
        char* stack_trace = NULL;
        unsigned int trace_size = 0;
        if (kc_common_get_call_stack(&stack_trace, &trace_size) == 0) {
            fprintf(log_file, "Call Stack:\n%s\n", stack_trace);
            kc_common_delete_buffer(&stack_trace);
        }
        #endif

        fprintf(log_file, "========================\n");
        fclose(log_file);
    }

    // Cleanup
    if (error_msg) kc_common_delete_buffer(&error_msg);
    if (category) kc_common_delete_buffer(&category);
    if (diagnostics) kc_common_delete_buffer(&diagnostics);
}

// Usage example
void demonstrate_error_logging() {
    unsigned int result = kc_gateway_create_persona(gateway, NULL, "test", 4, "test", 4, KC_SECURITY_LEVEL_HIGH, 1);
    if (result != 0) {
        log_error_with_context(result, "kc_gateway_create_persona");
    }
}

Error Recovery Strategies

// Implement different recovery strategies based on error type
unsigned int handle_error_with_recovery(unsigned int error_code, kc_gateway_t* gateway) {
    char* category = NULL;
    unsigned int category_size = 0;
    unsigned int severity = 0;

    kc_common_get_error_category(error_code, &category, &category_size);
    kc_common_get_error_severity(error_code, &severity);

    if (category) {
        if (strcmp(category, "NETWORK") == 0) {
            // Network error - try reconnection
            printf("Network error detected - attempting reconnection\n");
            kc_gateway_on_stop(gateway);

            // Wait before retry
            #ifdef _WIN32
                Sleep(5000); // 5 seconds
            #else
                sleep(5);
            #endif

            unsigned int restart_result = kc_gateway_on_start(gateway);
            kc_common_delete_buffer(&category);
            return restart_result;

        } else if (strcmp(category, "CRYPTO") == 0) {
            // Cryptographic error - may need key regeneration
            printf("Cryptographic error detected - check key validity\n");
            kc_common_delete_buffer(&category);
            return KC_ERROR_KEY_NOT_FOUND; // Indicate key issue

        } else if (strcmp(category, "STORAGE") == 0) {
            // Storage error - try alternative storage
            printf("Storage error detected - switching to backup storage\n");
            // Implementation would switch to backup storage
            kc_common_delete_buffer(&category);
            return KC_SUCCESS;

        } else if (strcmp(category, "VALIDATION") == 0) {
            // Validation error - user input issue
            printf("Validation error detected - check input parameters\n");
            kc_common_delete_buffer(&category);
            return error_code; // Return original error for user correction
        }

        kc_common_delete_buffer(&category);
    }

    // Unknown error category - log for investigation
    printf("Unknown error category - logging for analysis\n");
    log_error_with_context(error_code, "unknown_operation");

    return error_code;
}

Debug Mode Error Handling

// Enhanced error handling for debug builds
#ifdef DEBUG
void debug_error_handler(unsigned int error_code) {
    printf("\n*** DEBUG ERROR HANDLER ***\n");

    // Enable comprehensive logging
    kc_common_enable_debug_logging("/tmp/keychain_debug.log", 23, 5); // TRACE level

    // Get all error details
    char* error_msg = NULL;
    char* category = NULL;
    char* diagnostics = NULL;
    char* stack_trace = NULL;
    unsigned int msg_size, category_size, diag_size, trace_size;
    unsigned int severity = 0;

    kc_common_get_error_message(&error_msg, &msg_size, error_code);
    kc_common_get_error_category(error_code, &category, &category_size);
    kc_common_get_error_severity(error_code, &severity);
    kc_common_get_diagnostic_info(&diagnostics, &diag_size);
    kc_common_get_call_stack(&stack_trace, &trace_size);

    printf("Error Code: %u\n", error_code);
    printf("Message: %s\n", error_msg ? error_msg : "Unknown");
    printf("Category: %s\n", category ? category : "Unknown");
    printf("Severity: %u\n", severity);

    if (diagnostics) {
        printf("Diagnostics:\n%s\n", diagnostics);
    }

    if (stack_trace) {
        printf("Call Stack:\n%s\n", stack_trace);
    }

    printf("*** END DEBUG ERROR HANDLER ***\n\n");

    // Cleanup
    if (error_msg) kc_common_delete_buffer(&error_msg);
    if (category) kc_common_delete_buffer(&category);
    if (diagnostics) kc_common_delete_buffer(&diagnostics);
    if (stack_trace) kc_common_delete_buffer(&stack_trace);
}
#endif

Thread Safety

Error handling functions are thread-safe. Each thread maintains its own error context:

// Thread-safe error handling example
#include <pthread.h>

void* worker_thread(void* arg) {
    int thread_id = *(int*)arg;

    // Each thread has its own error context
    kc_common_clear_last_error();

    unsigned int result = kc_gateway_some_operation(gateway);
    if (result != 0) {
        unsigned int last_error = 0;
        kc_common_get_last_error(&last_error);
        printf("Thread %d error: %u\n", thread_id, last_error);
    }

    return NULL;
}

See Also

  • C++ Gateway Class - Exception-based error handling

  • {error-handling-best-practices}[Error Handling Best Practices]

  • {c-api-debugging}[C API Debugging Guide]