un-inception/clients/c/examples/error_handling.c
russell@unturf.com 1e01d09883 feat: per-client Makefile infrastructure for 4-mode testing
Add per-client Makefiles for C, Python, and Go with:
- CLI mode: Tests --help, arg parsing, syntax validation
- Library mode: Unit tests, import verification
- Integration mode: API contract validation (with credentials)
- Functional mode: Real-world scenario tests

C client:
- 22 library tests (SHA-256, HMAC-SHA256, detect_language)
- Full unsandbox.c implementation with examples

Python client (sync + async):
- Delegates to sync/ and async/ subdirectories
- pytest-based test suites with coverage
- Examples for concurrent execution, streaming

Go client:
- Delegates to sync/ and async/ subdirectories
- go test integration with vet and fmt

Also update detect-changes.sh to detect changes in
both root-level un.* files AND clients/ directory.
2026-01-15 16:39:56 -05:00

418 lines
14 KiB
C

/*
* error_handling.c - Proper error handling patterns
*
* Demonstrates comprehensive error handling patterns for unsandbox code execution.
* This example shows:
* - HTTP error responses (401, 429, 500)
* - Runtime errors in executed code
* - Timeout detection
* - Memory leak prevention
* - Retry strategies
* - Logging errors
*
* Compile:
* gcc -o error_handling error_handling.c -I../../.. -lcurl -lwebsockets -lssl -lcrypto
*
* Run:
* export UNSANDBOX_PUBLIC_KEY=unsb-pk-xxxxx
* export UNSANDBOX_SECRET_KEY=unsb-sk-xxxxx
* ./error_handling
*
* Expected output:
* Demonstrates various error scenarios and proper handling
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
/* ============================================================================
* Error type definitions
* ============================================================================ */
typedef enum {
ERROR_NONE = 0,
ERROR_INVALID_CREDS = 1,
ERROR_RATE_LIMITED = 2,
ERROR_TIMEOUT = 3,
ERROR_RUNTIME_ERROR = 4,
ERROR_SERVER_ERROR = 5,
ERROR_NETWORK = 6,
ERROR_OUT_OF_MEMORY = 7,
ERROR_UNKNOWN = 8
} ErrorType;
typedef struct {
ErrorType type;
int http_code;
char *message;
char *details;
} ExecutionError;
/* ============================================================================
* Helper functions
* ============================================================================ */
/* Create a new error structure */
static ExecutionError* error_create(ErrorType type, int http_code,
const char *message, const char *details) {
ExecutionError *err = malloc(sizeof(ExecutionError));
if (!err) return NULL;
err->type = type;
err->http_code = http_code;
err->message = message ? strdup(message) : NULL;
err->details = details ? strdup(details) : NULL;
return err;
}
/* Free error structure */
static void error_free(ExecutionError *err) {
if (!err) return;
free(err->message);
free(err->details);
free(err);
}
/* Parse HTTP response to determine error type */
static ErrorType parse_error_type(const char *response, int http_code) {
if (!response) {
if (http_code >= 500) return ERROR_SERVER_ERROR;
if (http_code == 429) return ERROR_RATE_LIMITED;
if (http_code == 401) return ERROR_INVALID_CREDS;
return ERROR_UNKNOWN;
}
/* Check for specific error patterns in response */
if (strstr(response, "401") || strstr(response, "Unauthorized")) {
return ERROR_INVALID_CREDS;
}
if (strstr(response, "429") || strstr(response, "rate_limit")) {
return ERROR_RATE_LIMITED;
}
if (strstr(response, "timeout") || strstr(response, "Timeout")) {
return ERROR_TIMEOUT;
}
if (strstr(response, "Traceback") || strstr(response, "Error") ||
strstr(response, "Exception") || strstr(response, "error")) {
return ERROR_RUNTIME_ERROR;
}
if (http_code >= 500) {
return ERROR_SERVER_ERROR;
}
return ERROR_UNKNOWN;
}
/* Print human-readable error message */
static void error_print(const ExecutionError *err) {
if (!err) {
printf("Error: NULL error object\n");
return;
}
printf("Error [%d]: ", err->http_code);
switch (err->type) {
case ERROR_INVALID_CREDS:
printf("Invalid Credentials\n");
printf(" Your API key or secret key is incorrect.\n");
printf(" Check UNSANDBOX_PUBLIC_KEY and UNSANDBOX_SECRET_KEY.\n");
break;
case ERROR_RATE_LIMITED:
printf("Rate Limited\n");
printf(" You've exceeded your rate limit.\n");
printf(" Wait before retrying. Check response for reset time.\n");
break;
case ERROR_TIMEOUT:
printf("Timeout\n");
printf(" Code execution took too long and was terminated.\n");
printf(" Optimize your code or increase timeout if available.\n");
break;
case ERROR_RUNTIME_ERROR:
printf("Runtime Error\n");
printf(" Your code raised an exception or error.\n");
printf(" Check the code logic.\n");
break;
case ERROR_SERVER_ERROR:
printf("Server Error\n");
printf(" The unsandbox server encountered an error.\n");
printf(" This is typically temporary. Retry later.\n");
break;
case ERROR_NETWORK:
printf("Network Error\n");
printf(" Could not reach the unsandbox server.\n");
printf(" Check your internet connection.\n");
break;
case ERROR_OUT_OF_MEMORY:
printf("Out of Memory\n");
printf(" malloc() failed. System is out of memory.\n");
break;
case ERROR_NONE:
printf("No Error\n");
break;
case ERROR_UNKNOWN:
default:
printf("Unknown Error\n");
break;
}
if (err->message) {
printf(" Message: %s\n", err->message);
}
if (err->details) {
printf(" Details: %s\n", err->details);
}
}
/* ============================================================================
* Error handling patterns
* ============================================================================ */
/*
* Pattern 1: Check for NULL return (allocation failure)
*/
static void pattern_null_check(void) {
printf("\nPattern 1: Check for NULL (memory allocation failure)\n");
printf("========================================================\n");
printf("char *result = execute_code(language, code, pk, sk);\n");
printf("if (!result) {\n");
printf(" ExecutionError *err = error_create(\n");
printf(" ERROR_OUT_OF_MEMORY, 0,\n");
printf(" \"Failed to allocate result buffer\",\n");
printf(" \"malloc() returned NULL\"\n");
printf(" );\n");
printf(" error_print(err);\n");
printf(" error_free(err);\n");
printf(" return 1;\n");
printf("}\n");
}
/*
* Pattern 2: Check for HTTP error codes in response
*/
static void pattern_http_error_check(void) {
printf("\nPattern 2: Check for HTTP errors in response\n");
printf("=============================================\n");
printf("char *result = execute_code(language, code, pk, sk);\n");
printf("if (!result) {\n");
printf(" fprintf(stderr, \"Execution failed\\\\n\");\n");
printf(" return 1;\n");
printf("}\n");
printf("\n");
printf("ErrorType err_type = parse_error_type(result, 0);\n");
printf("if (err_type != ERROR_NONE) {\n");
printf(" ExecutionError *err = error_create(\n");
printf(" err_type, 0, \"Execution failed\", result\n");
printf(" );\n");
printf(" error_print(err);\n");
printf(" error_free(err);\n");
printf(" free(result);\n");
printf(" return 1;\n");
printf("}\n");
printf("\n");
printf("// Success: process result\n");
printf("printf(\"Output: %%s\\\\n\", result);\n");
printf("free(result);\n");
}
/*
* Pattern 3: Retry on transient errors
*/
static void pattern_retry_logic(void) {
printf("\nPattern 3: Retry logic for transient errors\n");
printf("==========================================\n");
printf("const int MAX_RETRIES = 3;\n");
printf("const int RETRY_DELAY_MS = 1000;\n");
printf("\n");
printf("for (int attempt = 0; attempt < MAX_RETRIES; attempt++) {\n");
printf(" char *result = execute_code(language, code, pk, sk);\n");
printf(" if (!result) break; // Success\n");
printf("\n");
printf(" ErrorType err_type = parse_error_type(result, 0);\n");
printf("\n");
printf(" if (err_type == ERROR_SERVER_ERROR ||\n");
printf(" err_type == ERROR_NETWORK) {\n");
printf(" // Transient error: retry\n");
printf(" printf(\"Retry %%d/%%d after %%dms...\\\\n\", \n");
printf(" attempt + 1, MAX_RETRIES, RETRY_DELAY_MS);\n");
printf(" free(result);\n");
printf(" usleep(RETRY_DELAY_MS * 1000);\n");
printf(" continue;\n");
printf(" }\n");
printf("\n");
printf(" if (err_type == ERROR_RATE_LIMITED) {\n");
printf(" // Rate limit: exponential backoff\n");
printf(" int backoff_ms = RETRY_DELAY_MS * (1 << attempt);\n");
printf(" printf(\"Rate limited, backoff %%dms...\\\\n\", backoff_ms);\n");
printf(" free(result);\n");
printf(" usleep(backoff_ms * 1000);\n");
printf(" continue;\n");
printf(" }\n");
printf("\n");
printf(" if (err_type == ERROR_INVALID_CREDS ||\n");
printf(" err_type == ERROR_RUNTIME_ERROR) {\n");
printf(" // Permanent error: don't retry\n");
printf(" ExecutionError *err = error_create(\n");
printf(" err_type, 0, \"Execution failed\", result\n");
printf(" );\n");
printf(" error_print(err);\n");
printf(" error_free(err);\n");
printf(" free(result);\n");
printf(" return 1;\n");
printf(" }\n");
printf("\n");
printf(" // Success\n");
printf(" printf(\"Output: %%s\\\\n\", result);\n");
printf(" free(result);\n");
printf(" return 0;\n");
printf("}\n");
printf("\n");
printf("fprintf(stderr, \"Failed after %%d attempts\\\\n\", MAX_RETRIES);\n");
printf("return 1;\n");
}
/*
* Pattern 4: Timeout detection
*/
static void pattern_timeout_detection(void) {
printf("\nPattern 4: Detect and handle timeouts\n");
printf("=====================================\n");
printf("char *result = execute_code(language, code, pk, sk);\n");
printf("if (!result) {\n");
printf(" fprintf(stderr, \"Execution failed\\\\n\");\n");
printf(" return 1;\n");
printf("}\n");
printf("\n");
printf("// Check for timeout indicators\n");
printf("if (strstr(result, \"timeout\") ||\n");
printf(" strstr(result, \"Timeout\") ||\n");
printf(" strstr(result, \"timed out\") ||\n");
printf(" strstr(result, \"killed\")) {\n");
printf(" ExecutionError *err = error_create(\n");
printf(" ERROR_TIMEOUT, 0,\n");
printf(" \"Code execution timed out\",\n");
printf(" result\n");
printf(" );\n");
printf(" error_print(err);\n");
printf(" error_free(err);\n");
printf(" free(result);\n");
printf(" return 1;\n");
printf("}\n");
printf("\n");
printf("printf(\"Output: %%s\\\\n\", result);\n");
printf("free(result);\n");
}
/*
* Pattern 5: Logging errors to file
*/
static void pattern_error_logging(void) {
printf("\nPattern 5: Log errors to file\n");
printf("=============================\n");
printf("FILE *log_file = fopen(\"execution.log\", \"a\");\n");
printf("if (!log_file) {\n");
printf(" perror(\"Failed to open log file\");\n");
printf(" return 1;\n");
printf("}\n");
printf("\n");
printf("char *result = execute_code(language, code, pk, sk);\n");
printf("if (!result) {\n");
printf(" fprintf(log_file,\n");
printf(" \"[ERROR] Execution failed at %%s\\\\n\",\n");
printf(" __func__);\n");
printf(" fclose(log_file);\n");
printf(" return 1;\n");
printf("}\n");
printf("\n");
printf("ErrorType err_type = parse_error_type(result, 0);\n");
printf("if (err_type != ERROR_NONE) {\n");
printf(" fprintf(log_file,\n");
printf(" \"[ERROR] Execution error: %%s\\\\n\",\n");
printf(" result);\n");
printf("}\n");
printf("\n");
printf("free(result);\n");
printf("fclose(log_file);\n");
}
/* ============================================================================
* Main demonstration
* ============================================================================ */
int main(void) {
printf("Unsandbox Error Handling Example\n");
printf("=================================\n");
/* Get credentials */
const char *public_key = getenv("UNSANDBOX_PUBLIC_KEY");
const char *secret_key = getenv("UNSANDBOX_SECRET_KEY");
if (!public_key || !secret_key) {
fprintf(stderr, "Error: Credentials not found\n");
fprintf(stderr, "Please set UNSANDBOX_PUBLIC_KEY and UNSANDBOX_SECRET_KEY\n");
return 1;
}
printf("Credentials loaded\n\n");
/* Demonstrate error types */
printf("Error Types:\n");
printf("============\n");
ExecutionError *err;
err = error_create(ERROR_INVALID_CREDS, 401, "Invalid credentials", NULL);
printf("\n1. ");
error_print(err);
error_free(err);
err = error_create(ERROR_RATE_LIMITED, 429, "Rate limited", "Reset in 60 seconds");
printf("\n2. ");
error_print(err);
error_free(err);
err = error_create(ERROR_TIMEOUT, 0, "Execution timeout", "Killed after 300 seconds");
printf("\n3. ");
error_print(err);
error_free(err);
err = error_create(ERROR_RUNTIME_ERROR, 0, "Runtime error",
"Traceback: ZeroDivisionError: division by zero");
printf("\n4. ");
error_print(err);
error_free(err);
err = error_create(ERROR_SERVER_ERROR, 500, "Server error", NULL);
printf("\n5. ");
error_print(err);
error_free(err);
/* Show patterns */
pattern_null_check();
pattern_http_error_check();
pattern_retry_logic();
pattern_timeout_detection();
pattern_error_logging();
printf("\n\nKey Points:\n");
printf("===========\n");
printf("1. Always check return values for NULL\n");
printf("2. Parse error responses to determine error type\n");
printf("3. Distinguish transient vs permanent errors\n");
printf("4. Implement retry logic with exponential backoff\n");
printf("5. Detect timeouts by checking response content\n");
printf("6. Log errors for debugging\n");
printf("7. Always free allocated memory\n");
return 0;
}