Error Handling

Comprehensive error handling with traditional Rexx SIGNAL statements and enhanced error context information.

Traditional vs Enhanced Error Handling

Standard Rexx SIGNAL Behavior

Traditional Rexx uses GOTO-style error handling with SIGNAL ON ERROR:

SIGNAL ON ERROR NAME ErrorHandler
-- Some operation that might fail
-- If error occurs, execution jumps to ErrorHandler
-- Lost: call stack, variable context, error details

ErrorHandler:
-- Only knows: an error happened somewhere
SAY "An error occurred"

Enhanced Error Handling (Our Implementation)

Our implementation extends Rexx with enhanced error context while maintaining traditional SIGNAL semantics:

SIGNAL ON ERROR NAME ErrorHandler
LET x = 10
LET operation = "division"
LET result = DIVIDE x=x y=0  -- Error occurs here

ErrorHandler:
-- Enhanced: Full error context available
LET error_line = ERROR_LINE           -- Returns: 4
LET error_function = ERROR_FUNCTION   -- Returns: "DIVIDE"
LET error_message = ERROR_MESSAGE     -- Returns: "Division by zero"
LET error_stack = ERROR_STACK         -- Returns: JavaScript stack trace
LET error_vars = ERROR_VARIABLES      -- Returns: {"x": 10, "operation": "division"}

SAY "Error in " || error_function || " at line " || error_line
SAY "Context variables: " || error_vars
SAY "JavaScript stack trace:"
SAY error_stack

Error Context Functions

Core Error Information

ERROR_LINE - Line Number

SIGNAL ON ERROR NAME Handler
-- Line 2
-- Line 3  
LET result = JSON_PARSE text="invalid"  -- Line 4 - error occurs here

Handler:
LET line_num = ERROR_LINE  -- Returns: 4

ERROR_MESSAGE - Error Message

SIGNAL ON ERROR NAME Handler
LET result = DIVIDE x=10 y=0

Handler:
LET msg = ERROR_MESSAGE  -- Returns: "Division by zero"

ERROR_FUNCTION - Function Name

SIGNAL ON ERROR NAME Handler
LET hash = SHA256 data="test"  -- Fails if crypto not available

Handler:
LET func = ERROR_FUNCTION  -- Returns: "SHA256"

ERROR_COMMAND - Complete Command Text

SIGNAL ON ERROR NAME Handler
LET result = JSON_PARSE text="malformed {}" pretty=true

Handler:
LET cmd = ERROR_COMMAND  
-- Returns: 'LET result = JSON_PARSE text="malformed {}" pretty=true'

Advanced Error Context

ERROR_VARIABLES - Variable Snapshot

SIGNAL ON ERROR NAME Handler
LET user = "john_doe"
LET config = '{"debug": true}'
LET count = 42
LET result = SOME_FAILING_FUNCTION

Handler:
LET vars_json = ERROR_VARIABLES
LET vars = JSON_PARSE text=vars_json
-- vars now contains: {"user": "john_doe", "config": "{\"debug\": true}", "count": 42}

-- Access individual variables
LET failed_user = ARRAY_GET array=vars key="user"      -- "john_doe"
LET failed_count = ARRAY_GET array=vars key="count"    -- 42

ERROR_STACK - JavaScript Stack Trace

SIGNAL ON ERROR NAME Handler
LET result = DOM_CLICK selector="#missing-element"

Handler:
LET js_stack = ERROR_STACK
SAY "JavaScript call stack:"
SAY js_stack

Output:

JavaScript call stack:
Error: DOM click failed: Element not found: #missing-element
    at DOM_CLICK (/path/to/interpreter.js:2920:17)
    at Interpreter.executeFunctionCall (/path/to/interpreter.js:3373:14)
    at Interpreter.executeCommand (/path/to/interpreter.js:3326:39)
    at Interpreter.executeCommands (/path/to/interpreter.js:3272:35)
    at async Interpreter.run (/path/to/interpreter.js:3252:14)

ERROR_TIMESTAMP - When Error Occurred

SIGNAL ON ERROR NAME Handler
LET result = FAILING_OPERATION

Handler:
LET error_time = ERROR_TIMESTAMP  -- "2025-08-26T07:30:15.123Z"

ERROR_DETAILS - Summary Object

SIGNAL ON ERROR NAME Handler
LET result = FAILING_OPERATION

Handler:
LET details_json = ERROR_DETAILS
LET details = JSON_PARSE text=details_json
-- details contains:
-- {
--   "line": 3,
--   "message": "Operation failed",
--   "function": "FAILING_OPERATION",
--   "command": "LET result = FAILING_OPERATION",
--   "timestamp": "2025-08-26T07:30:15.123Z",
--   "hasStack": true
-- }

SIGNAL Statement Syntax

SIGNAL ON ERROR

Enable error handling and specify a label to jump to when errors occur.

Basic Syntax:

SIGNAL ON ERROR NAME label_name

Default Label (ERROR):

SIGNAL ON ERROR  -- Uses default label "ERROR"

-- Your code here
LET result = RISKY_OPERATION

EXIT

ERROR:
SAY "An error occurred"
LET error_msg = ERROR_MESSAGE
SAY "Details: " || error_msg

Custom Label:

SIGNAL ON ERROR NAME MyErrorHandler

-- Your code here
LET result = RISKY_OPERATION

EXIT

MyErrorHandler:
SAY "Custom error handler activated"
LET line_num = ERROR_LINE
LET func_name = ERROR_FUNCTION
SAY "Error in " || func_name || " at line " || line_num

SIGNAL OFF ERROR

Disable error handling and let errors propagate normally.

SIGNAL ON ERROR NAME Handler
-- Error handling active

SIGNAL OFF ERROR
-- Error handling disabled - errors will be thrown normally

LET result = FAILING_OPERATION  -- Will throw unhandled error

Handler:
-- This won't be reached

Multiple Error Handlers

You can change error handlers during execution:

SIGNAL ON ERROR NAME InitHandler
LET config = LOAD_CONFIG

SIGNAL ON ERROR NAME ProcessHandler  
LET data = PROCESS_DATA config=config

SIGNAL ON ERROR NAME CleanupHandler
CLEANUP_RESOURCES

EXIT

InitHandler:
SAY "Error during initialization"
-- Handle init errors
EXIT

ProcessHandler:
SAY "Error during processing" 
-- Handle processing errors
EXIT

CleanupHandler:
SAY "Error during cleanup"
-- Handle cleanup errors

Error Recovery Patterns

Smart Error Recovery

Use error context to implement intelligent error recovery:

SIGNAL ON ERROR NAME SmartRecovery
LET retry_count = 0
LET max_retries = 3

ProcessData:
LET result = NETWORK_OPERATION url="https://api.example.com/data"
EXIT

SmartRecovery:
LET error_func = ERROR_FUNCTION
LET error_msg = ERROR_MESSAGE
LET vars = JSON_PARSE text=ERROR_VARIABLES
LET current_retries = ARRAY_GET array=vars key="retry_count"

IF error_func = "NETWORK_OPERATION" THEN
    IF current_retries < max_retries THEN
        LET retry_count = current_retries + 1
        SAY "Network error, retrying... (attempt " || retry_count || ")"
        WAIT milliseconds=1000
        SIGNAL ProcessData  -- Jump back to retry
    ELSE
        SAY "Network operation failed after " || max_retries || " attempts"
        SAY "Final error: " || error_msg
    ENDIF
ELSE
    SAY "Unexpected error in " || error_func || ": " || error_msg
ENDIF

Fallback Strategies

SIGNAL ON ERROR NAME FallbackHandler
LET primary_method = "crypto"

ProcessSecurity:
SELECT
    WHEN primary_method = "crypto" THEN
        LET hash = SHA256 data="sensitive_data"
    WHEN primary_method = "basic" THEN
        LET hash = SIMPLE_HASH data="sensitive_data"
    OTHERWISE
        LET hash = "no_security"
END
EXIT

FallbackHandler:
LET error_func = ERROR_FUNCTION
LET vars = JSON_PARSE text=ERROR_VARIABLES
LET current_method = ARRAY_GET array=vars key="primary_method"

SELECT
    WHEN error_func = "SHA256" AND current_method = "crypto" THEN
        SAY "Crypto not available, falling back to basic hashing"
        LET primary_method = "basic"
        SIGNAL ProcessSecurity
    WHEN error_func = "SIMPLE_HASH" AND current_method = "basic" THEN  
        SAY "Basic hashing failed, disabling security"
        LET primary_method = "none"
        SIGNAL ProcessSecurity
    OTHERWISE
        SAY "All security methods failed: " || ERROR_MESSAGE
END

Error Logging and Monitoring

SIGNAL ON ERROR NAME ErrorLogger

-- Main application logic
LET result = COMPLEX_OPERATION param1="value1" param2="value2"

EXIT

ErrorLogger:
-- Capture comprehensive error information
LET timestamp = ERROR_TIMESTAMP
LET line_num = ERROR_LINE
LET func_name = ERROR_FUNCTION  
LET error_msg = ERROR_MESSAGE
LET command = ERROR_COMMAND
LET vars_json = ERROR_VARIABLES
LET stack_trace = ERROR_STACK

-- Create error report
LET error_report = "=== ERROR REPORT ==="
LET error_report = error_report || "\nTimestamp: " || timestamp
LET error_report = error_report || "\nLine: " || line_num
LET error_report = error_report || "\nFunction: " || func_name
LET error_report = error_report || "\nCommand: " || command
LET error_report = error_report || "\nMessage: " || error_msg
LET error_report = error_report || "\nVariables: " || vars_json
LET error_report = error_report || "\nStack Trace:\n" || stack_trace

-- Log to console
SAY error_report

-- Could also send to logging service
-- SEND_TO_LOGGER report=error_report level="error"

-- Graceful shutdown
SAY "\nApplication terminating due to error"
EXIT

Integration with Built-in Functions

DOM Function Error Handling

SIGNAL ON ERROR NAME DOMErrorHandler

-- DOM automation that might fail
LET element_count = DOM_QUERY selector=".items" operation="count"

DO i = 1 TO element_count
    LET item_selector = ".items:nth-child(" || i || ")"
    DOM_CLICK selector=item_selector
    
    -- Wait for response
    LET success = DOM_WAIT_FOR selector=".success" timeout=2000
    IF success = false THEN
        -- Force an error to trigger handler
        DOM_CLICK selector="#non-existent-element"
    ENDIF
END

EXIT

DOMErrorHandler:
LET error_func = ERROR_FUNCTION
LET error_line = ERROR_LINE
LET vars = JSON_PARSE text=ERROR_VARIABLES
LET current_i = ARRAY_GET array=vars key="i"

SAY "DOM operation failed:"
SAY "  Function: " || error_func
SAY "  Line: " || error_line  
SAY "  Processing item: " || current_i
SAY "  Message: " || ERROR_MESSAGE

-- Attempt recovery
IF error_func = "DOM_CLICK" THEN
    SAY "Click failed, trying alternative selector"
    -- Could implement alternative clicking strategy
ELSE IF error_func = "DOM_WAIT_FOR" THEN
    SAY "Wait timeout, continuing anyway"
    -- Continue with next iteration
ENDIF

JSON Function Error Handling

SIGNAL ON ERROR NAME JSONErrorHandler
LET config_text = '{"settings": {"timeout": 5000, "debug": true}'  -- Invalid JSON

ParseConfig:
LET config = JSON_PARSE text=config_text
LET timeout = ARRAY_GET array=config key="timeout"
EXIT

JSONErrorHandler:
LET error_func = ERROR_FUNCTION
LET vars = JSON_PARSE text=ERROR_VARIABLES
LET bad_json = ARRAY_GET array=vars key="config_text"

IF error_func = "JSON_PARSE" THEN
    SAY "JSON parsing failed for:"
    SAY bad_json
    SAY "Error: " || ERROR_MESSAGE
    
    -- Use default configuration
    SAY "Using default configuration"
    LET config = JSON_PARSE text='{"timeout": 3000, "debug": false}'
    LET timeout = ARRAY_GET array=config key="timeout"
    
    SAY "Continuing with timeout: " || timeout
ENDIF

Mathematical Function Error Handling

SIGNAL ON ERROR NAME MathErrorHandler
LET numbers = '[1, 2, 0, 4, 5]'
LET results = '[]'

ProcessNumbers:
LET num_array = JSON_PARSE text=numbers
LET count = ARRAY_LENGTH array=num_array

DO i = 1 TO count
    LET num = ARRAY_GET array=num_array index=i
    LET result = DIVIDE x=100 y=num
    LET results = ARRAY_PUSH array=results item=result
END

LET final_results = JSON_STRINGIFY data=results
SAY "Results: " || final_results
EXIT

MathErrorHandler:
LET error_func = ERROR_FUNCTION
LET vars = JSON_PARSE text=ERROR_VARIABLES
LET current_i = ARRAY_GET array=vars key="i"
LET current_num = ARRAY_GET array=vars key="num"

IF error_func = "DIVIDE" AND current_num = 0 THEN
    SAY "Division by zero at position " || current_i
    SAY "Skipping this number and continuing"
    
    -- Add a placeholder and continue
    LET current_results = ARRAY_GET array=vars key="results"
    LET results = ARRAY_PUSH array=current_results item="undefined"
    
    -- Continue the loop (would need more complex loop handling)
    SAY "Added placeholder, continuing processing"
ELSE
    SAY "Unexpected math error: " || ERROR_MESSAGE
ENDIF

Testing Error Scenarios

Error Handler Testing

-- Test various error conditions
SIGNAL ON ERROR NAME TestErrorHandler
LET test_case = 1

RunTests:
SELECT  
    WHEN test_case = 1 THEN
        SAY "Testing JSON parsing error..."
        LET result = JSON_PARSE text="invalid json"
    WHEN test_case = 2 THEN
        SAY "Testing DOM error..."
        DOM_CLICK selector="#nonexistent"
    WHEN test_case = 3 THEN
        SAY "Testing math error..."  
        LET result = DIVIDE x=1 y=0
    OTHERWISE
        SAY "All tests completed"
        EXIT
END

TestErrorHandler:
LET error_func = ERROR_FUNCTION
LET error_msg = ERROR_MESSAGE
LET current_test = ERROR_VARIABLES
LET test_num = ARRAY_GET array=JSON_PARSE(text=current_test) key="test_case"

SAY "Test " || test_num || " - " || error_func || " error caught:"
SAY "  Message: " || error_msg
SAY "  Line: " || ERROR_LINE

-- Move to next test
LET test_case = test_num + 1
SIGNAL RunTests

Best Practices

1. Always Provide Meaningful Error Handling

-- Good: Specific error handling
SIGNAL ON ERROR NAME ConfigErrorHandler

-- Bad: Generic error handling that loses context
-- SIGNAL ON ERROR NAME GenericHandler

2. Use Error Context for Smart Recovery

-- Good: Use error context to make informed decisions
ErrorHandler:
LET error_func = ERROR_FUNCTION
IF error_func = "NETWORK_OPERATION" THEN
    -- Handle network errors specifically
ELSE IF error_func = "FILE_OPERATION" THEN
    -- Handle file errors specifically
ENDIF

-- Bad: Generic error handling
ErrorHandler:
SAY "Something went wrong"

3. Log Comprehensive Error Information

-- Good: Capture full context
ErrorHandler:
SAY "Error occurred at line " || ERROR_LINE
SAY "Function: " || ERROR_FUNCTION  
SAY "Command: " || ERROR_COMMAND
SAY "Variables: " || ERROR_VARIABLES
SAY "Stack trace:"
SAY ERROR_STACK

-- Bad: Minimal error info
ErrorHandler:
SAY "Error: " || ERROR_MESSAGE

4. Clean Up Resources in Error Handlers

SIGNAL ON ERROR NAME CleanupHandler
LET file_handle = OPEN_FILE path="data.txt"
-- Process file
LET result = PROCESS_FILE handle=file_handle

CLOSE_FILE handle=file_handle
EXIT

CleanupHandler:
-- Always clean up, even on error
LET vars = JSON_PARSE text=ERROR_VARIABLES
LET handle = ARRAY_GET array=vars key="file_handle"
IF handle THEN
    CLOSE_FILE handle=handle
ENDIF

SAY "Error occurred: " || ERROR_MESSAGE
SAY "Resources cleaned up"

5. Use Nested Error Handling for Complex Operations

MainOperation:
SIGNAL ON ERROR NAME MainErrorHandler

PreprocessingPhase:
SIGNAL ON ERROR NAME PreprocessingErrorHandler
-- Preprocessing code
SIGNAL ON ERROR NAME MainErrorHandler  -- Restore main handler

ProcessingPhase:  
SIGNAL ON ERROR NAME ProcessingErrorHandler
-- Processing code
SIGNAL ON ERROR NAME MainErrorHandler  -- Restore main handler

-- Continue with other phases...
EXIT

PreprocessingErrorHandler:
SAY "Preprocessing failed: " || ERROR_MESSAGE
-- Handle preprocessing errors
SIGNAL ProcessingPhase  -- Skip to next phase

ProcessingErrorHandler:
SAY "Processing failed: " || ERROR_MESSAGE  
-- Handle processing errors
EXIT

MainErrorHandler:
SAY "Operation failed: " || ERROR_MESSAGE
SAY "Stack trace:"
SAY ERROR_STACK

Debugging Tips

Using Stack Traces for Debugging

The JavaScript stack traces help identify:

  • Exact function call chain leading to the error
  • Line numbers in the interpreter source code
  • JavaScript function names for debugging interpreter issues
  • Async call patterns showing Promise resolution chains

Variable State Analysis

The ERROR_VARIABLES snapshot helps with:

  • Understanding error context - what data led to the failure
  • Debugging variable values at the exact moment of error
  • Implementing smart recovery based on variable state
  • Logging for production debugging

Error Pattern Recognition

Common error patterns and their contexts:

  • DOM errors: Usually indicate missing elements or incorrect selectors
  • JSON errors: Invalid JSON syntax or structure
  • Network errors: Connectivity issues or API failures
  • Math errors: Division by zero or invalid numeric operations

This comprehensive error handling system bridges traditional Rexx SIGNAL semantics with modern JavaScript debugging capabilities, providing developers with the best tools for building robust, debuggable Rexx applications.