T5.2: Handles and Logs Runtime Errors

Knowledge Review - InterSystems ObjectScript Specialist

1. TRY-CATCH blocks

Key Points

  • TRY block: Contains protected code that may generate errors
  • CATCH block: Receives an exception object derived from %Exception.AbstractException
  • Exception properties: ex.Name (error name), ex.Code (error code), ex.Data (additional data), ex.Location (where error occurred)
  • Multiple CATCH: Only one CATCH block per TRY; use IF/ELSEIF inside CATCH to handle different error types
  • Nesting: TRY-CATCH blocks can be nested for granular error handling
  • No stack level: TRY blocks do not create a new stack level

Detailed Notes

Overview

TRY-CATCH is the recommended error handling mechanism in modern ObjectScript. It provides structured, readable, and maintainable error handling using exception objects that carry detailed information about what went wrong and where.

Basic TRY-CATCH Syntax

ClassMethod BasicExample()
{
    TRY {
        // Protected code
        Set x = 1 / 0  // Will cause <DIVIDE> error
        Write "This line never executes", !
    }
    CATCH ex {
        Write "Error: ", ex.Name, !           // <DIVIDE>
        Write "Code: ", ex.Code, !            // Error code
        Write "Location: ", ex.Location, !    // Where it happened
        Write "Data: ", ex.Data, !            // Additional info
    }
    Write "Execution continues here", !
}

Exception Object Properties and Methods

The CATCH variable is an instance of %Exception.AbstractException (or a subclass). Key properties and methods:

ClassMethod ExamineException()
{
    TRY {
        Set ^ReadOnlyGlobal(1) = "test"  // Assuming <PROTECT> error
    }
    CATCH ex {
        // Properties
        Write "Name:     ", ex.Name, !        // Error name (e.g., <PROTECT>)
        Write "Code:     ", ex.Code, !        // Numeric error code
        Write "Location: ", ex.Location, !    // label+offset^routine
        Write "Data:     ", ex.Data, !        // Context-specific data

        // Methods
        Write "Display:  ", ex.DisplayString(), !  // Full formatted string
        Set sc = ex.AsStatus()                     // Convert to %Status
        Write "Status:   ", $SYSTEM.Status.GetErrorText(sc), !

        // As a SQL code
        Write "SQL Code: ", ex.AsSQLCODE(), !
        Write "SQL Msg:  ", ex.AsSQLMessage(), !
    }
}

Nested TRY-CATCH Blocks

ClassMethod NestedExample()
{
    TRY {
        Write "Outer TRY", !

        TRY {
            Write "Inner TRY", !
            // Generate an error
            Set x = $PIECE("", ",", -1)  // <FUNCTION> error
        }
        CATCH innerEx {
            Write "Inner CATCH: ", innerEx.Name, !
            // Can rethrow to outer CATCH
            If innerEx.Name = "<SERIOUS>" {
                THROW innerEx
            }
            // Otherwise handle it here
        }

        Write "Continues in outer TRY", !
    }
    CATCH outerEx {
        Write "Outer CATCH: ", outerEx.Name, !
    }
}

Handling Different Error Types Within CATCH

ClassMethod HandleByType()
{
    TRY {
        Do ..RiskyOperation()
    }
    CATCH ex {
        If ex.Name = "<DIVIDE>" {
            Write "Division by zero detected", !
        }
        ElseIf ex.Name = "<UNDEFINED>" {
            Write "Undefined variable: ", ex.Data, !
        }
        ElseIf ex.Name = "<SUBSCRIPT>" {
            Write "Invalid subscript access", !
        }
        Else {
            Write "Unexpected error: ", ex.DisplayString(), !
            // Rethrow unexpected errors
            THROW ex
        }
    }
}

2. Throwing exceptions

Key Points

  • THROW command: Throws an exception object, transferring control to the nearest CATCH block
  • %Exception.General: General-purpose exception class for user-defined errors
  • $$$ThrowStatus: Macro that throws an exception created from a %Status value
  • $$$ThrowOnError: Macro that throws only if the status is an error (does nothing if OK)
  • Custom exception classes: Can extend %Exception.AbstractException for application-specific errors
  • Rethrow: Catch an exception and THROW it again to propagate to an outer handler

Detailed Notes

Overview

Throwing exceptions allows you to signal error conditions explicitly, rather than relying only on system-generated errors. This is essential for creating robust applications with clear error boundaries.

Throwing %Exception.General

ClassMethod ValidateAge(age As %Integer) As %Status
{
    TRY {
        If age < 0 {
            Throw ##class(%Exception.General).%New(
                "InvalidAge",    // Name
                5001,            // Code (application-defined)
                ,                // Location (auto-filled)
                "Age cannot be negative: " _ age)  // Data
        }
        If age > 150 {
            Throw ##class(%Exception.General).%New(
                "InvalidAge", 5001, , "Unrealistic age: " _ age)
        }
        Write "Age is valid: ", age, !
        Return $$$OK
    }
    CATCH ex {
        Write "Validation error: ", ex.Name, !
        Write "Details: ", ex.Data, !
        Return ex.AsStatus()
    }
}

Using $$$ThrowStatus Macro

The $$$ThrowStatus macro converts a %Status value into an exception and throws it. This is essential for bridging status-based code and exception-based code.

Include %occStatus

ClassMethod SaveRecord() As %Status
{
    TRY {
        Set obj = ##class(MyApp.Person).%New()
        Set obj.Name = "John Doe"
        Set sc = obj.%Save()

        // If sc is an error, throw it as an exception
        $$$ThrowOnError(sc)

        Write "Saved successfully with ID: ", obj.%Id(), !
        Return $$$OK
    }
    CATCH ex {
        Write "Save failed: ", ex.DisplayString(), !
        Return ex.AsStatus()
    }
}

$$$ThrowOnError vs $$$ThrowStatus

Include %occStatus

ClassMethod CompareThrowMacros()
{
    TRY {
        Set sc = $$$OK

        // $$$ThrowOnError: Only throws if sc is an error
        // Does NOTHING if sc is $$$OK -- safe to use always
        $$$ThrowOnError(sc)   // No effect, sc is OK

        Set sc = $$$ERROR($$$GeneralError, "Something went wrong")

        // $$$ThrowStatus: ALWAYS throws, even if sc is OK
        // Use this only when you KNOW sc is an error
        // $$$ThrowStatus(sc)  // Would always throw

        // $$$ThrowOnError: Throws because sc is now an error
        $$$ThrowOnError(sc)   // This throws!

        Write "Never reached", !
    }
    CATCH ex {
        Write "Caught: ", ex.DisplayString(), !
    }
}

Rethrowing Exceptions

ClassMethod OuterMethod()
{
    TRY {
        Do ..InnerMethod()
    }
    CATCH ex {
        Write "Final handler: ", ex.DisplayString(), !
    }
}

ClassMethod InnerMethod() [ Private ]
{
    TRY {
        // Some risky operation
        Set value = $LISTGET($LISTBUILD(), 5)
    }
    CATCH ex {
        // Log the error
        Set ^ErrorLog($INCREMENT(^ErrorLog)) = ex.DisplayString()
        // Rethrow to let caller handle it
        THROW ex
    }
}

3. Application error log

Key Points

  • ^ERRORS global: System global that stores application errors automatically when unhandled errors occur
  • $SYSTEM.OBJ.DisplayError(): Displays human-readable error text from a %Status value
  • Management Portal: Application Error Log viewer at System Operation > System Logs > Application Error Log
  • Error structure: ^ERRORS stores error name, location, stack trace, variables, and timestamp
  • ^%ERN: Counter global tracking the number of errors per namespace
  • Custom logging: Best practice is to supplement system logging with application-specific error logs

Detailed Notes

Overview

InterSystems IRIS automatically logs unhandled errors in the ^ERRORS global. Additionally, developers should implement structured error logging within their applications for comprehensive diagnostics.

The ^ERRORS Global

When an unhandled error occurs (no TRY-CATCH or $ZTRAP in effect), IRIS logs the error in the ^ERRORS global in the current namespace. The structure contains:

// Viewing the error log from Terminal
ZWrite ^ERRORS

// Structure of ^ERRORS:
// ^ERRORS(date, index) = errorName
// ^ERRORS(date, index, "m") = stack trace details
// where date is in $HOROLOG format and index is sequential

Management Portal Application Error Log

Navigate to System Operation > System Logs > Application Error Log to view a formatted table of application errors. This page shows:

  • Date and time of each error
  • Error name (e.g., <UNDEFINED>, <SUBSCRIPT>)
  • Location where the error occurred
  • Namespace where the error happened
  • Process ID and username

Using $SYSTEM.Status Methods for Error Display

Include %occStatus

ClassMethod DemonstrateErrorDisplay()
{
    // Create an error status
    Set sc = $$$ERROR($$$GeneralError, "Database connection failed")

    // DisplayError - writes to current device
    Do $SYSTEM.Status.DisplayError(sc)
    // Output: ERROR #5001: Database connection failed

    // GetErrorText - returns error as a string
    Set errorText = $SYSTEM.Status.GetErrorText(sc)
    Write "Error text: ", errorText, !

    // For multi-status errors, get individual components
    Set sc2 = $$$ERROR($$$GeneralError, "Additional problem")
    Set combinedSc = $SYSTEM.Status.AppendStatus(sc, sc2)

    // GetOneStatusText gets a specific error from a compound status
    Set firstError = $SYSTEM.Status.GetOneStatusText(combinedSc, 1)
    Set secondError = $SYSTEM.Status.GetOneStatusText(combinedSc, 2)
    Write "First:  ", firstError, !
    Write "Second: ", secondError, !
}

Logging Exceptions to the Application Error Log

The simplest way to log a caught exception to ^ERRORS (viewable in the Management Portal and via Do ^%ER) is to call the .Log() method on the exception object:

TRY {
    // Business logic
    Set result = 100 / 0
}
CATCH ex {
    // Log to ^ERRORS (calls LOG^%ETN internally)
    Do ex.Log()
    // Continue with error handling...
    Return ex.AsStatus()
}

The %Exception.AbstractException.Log() method calls LOG^%ETN to record the exception in the system error log. You can then view logged errors:

  • Terminal: Do ^%ER utility
  • Management Portal: System Operation > System Logs > Application Error Log

This is the recommended approach for logging caught exceptions, since unhandled exceptions are logged automatically but caught exceptions are not unless you explicitly call .Log().

Building a Custom Application Error Log

ClassMethod LogError(
    className As %String,
    methodName As %String,
    ex As %Exception.AbstractException)
{
    Set timestamp = $ZDATETIME($HOROLOG, 3)
    Set key = $INCREMENT(^MyApp.ErrorLog)

    Set ^MyApp.ErrorLog(key, "timestamp") = timestamp
    Set ^MyApp.ErrorLog(key, "class") = className
    Set ^MyApp.ErrorLog(key, "method") = methodName
    Set ^MyApp.ErrorLog(key, "error") = ex.DisplayString()
    Set ^MyApp.ErrorLog(key, "name") = ex.Name
    Set ^MyApp.ErrorLog(key, "code") = ex.Code
    Set ^MyApp.ErrorLog(key, "location") = ex.Location
    Set ^MyApp.ErrorLog(key, "data") = ex.Data

    // Also log the stack trace
    For level = 0:1:$STACK(-1) {
        Set ^MyApp.ErrorLog(key, "stack", level) =
            $STACK(level, "PLACE") _ " : " _ $STACK(level, "MCODE")
    }
}

ClassMethod ProcessData()
{
    TRY {
        // Business logic here
        Set result = 100 / 0
    }
    CATCH ex {
        Do ..LogError("MyApp.Processor", "ProcessData", ex)
        // Optionally rethrow or return error status
        Return ex.AsStatus()
    }
}

4. $STACK for tracing

Key Points

  • $STACK(-1): Returns the current stack depth (number of levels on the execution stack)
  • $STACK(level, "PLACE"): Returns the location (label+offset^routine) at the specified stack level
  • $STACK(level, "MCODE"): Returns the actual source code at the specified stack level
  • $STACK(level, "ECODE"): Returns the error code at a given level (if an error occurred there)
  • Loop pattern: Loop from 0 to $STACK(-1) to dump the full call stack
  • Diagnostic use: Essential for understanding call chains during error handling

Detailed Notes

Overview

The $STACK special variable provides programmatic access to the execution call stack. During error handling, $STACK lets you capture a complete trace of the call chain that led to the error, which is invaluable for debugging.

Basic $STACK Usage

ClassMethod ShowStack()
{
    Write "Current stack depth: ", $STACK(-1), !
    Write !
    For level = 0:1:$STACK(-1) {
        Write "Level ", level, ": "
        Write $STACK(level, "PLACE")
        Write !
    }
}

Full Stack Dump During Error Handling

ClassMethod MethodA()
{
    TRY {
        Do ..MethodB()
    }
    CATCH ex {
        Write "Error: ", ex.DisplayString(), !
        Write "--- Stack Trace ---", !
        Do ..DumpStack()
    }
}

ClassMethod MethodB() [ Private ]
{
    Do ..MethodC()
}

ClassMethod MethodC() [ Private ]
{
    // Cause an error
    Set x = undefinedVariable  // <UNDEFINED> error
}

ClassMethod DumpStack()
{
    Set depth = $STACK(-1)
    Write "Stack depth: ", depth, !
    For level = 0:1:depth {
        Write "  Level ", level, ":", !
        Write "    PLACE: ", $STACK(level, "PLACE"), !
        Write "    MCODE: ", $STACK(level, "MCODE"), !
        Set ecode = $STACK(level, "ECODE")
        If ecode '= "" {
            Write "    ECODE: ", ecode, !
        }
    }
}

Output would look like:

Error: <UNDEFINED>zMethodC+1^MyApp.Debug.1 *undefinedVariable
--- Stack Trace ---
Stack depth: 3
  Level 0:
    PLACE: +1^MyApp.Debug.1
    MCODE: Do ..MethodB()
  Level 1:
    PLACE: zMethodB+1^MyApp.Debug.1
    MCODE: Do ..MethodC()
  Level 2:
    PLACE: zMethodC+1^MyApp.Debug.1
    MCODE: Set x = undefinedVariable
    ECODE: <UNDEFINED>

$STACK Parameters Reference

SyntaxReturns
$STACK(-1)Current stack depth (integer)
$STACK(n, "PLACE")Location at level n (label+offset^routine)
$STACK(n, "MCODE")Source code at level n
$STACK(n, "ECODE")Error code at level n (empty if no error)

Integrating $STACK with Error Logging

ClassMethod CaptureStackTrace() As %String
{
    Set trace = ""
    Set depth = $STACK(-1)
    For level = 0:1:depth {
        Set line = "Level " _ level _ ": "
        Set line = line _ $STACK(level, "PLACE")
        Set mcode = $STACK(level, "MCODE")
        If mcode '= "" {
            Set line = line _ " [" _ mcode _ "]"
        }
        Set trace = trace _ line _ $CHAR(10)
    }
    Return trace
}

ClassMethod HandleError()
{
    TRY {
        Do ..SomeOperation()
    }
    CATCH ex {
        Set stackTrace = ..CaptureStackTrace()
        // Store in error log global
        Set key = $INCREMENT(^ErrorLog)
        Set ^ErrorLog(key) = ex.DisplayString()
        Set ^ErrorLog(key, "stack") = stackTrace
        Set ^ErrorLog(key, "time") = $ZDATETIME($HOROLOG, 3)
    }
}

5. Converting error status codes

Key Points

  • $$$ISERR(sc): Macro that returns true (1) if the status represents an error
  • $$$ISOK(sc): Macro that returns true (1) if the status represents success
  • $SYSTEM.Status.GetErrorText(sc): Returns a human-readable error string
  • $SYSTEM.Status.DisplayError(sc): Writes the error text to the current device
  • $SYSTEM.Status.GetOneStatusText(sc, n): Returns the nth error from a compound status
  • $$$ERROR(): Macro to create a new error %Status value with a specific error code and message

Detailed Notes

Overview

%Status is a fundamental data type in InterSystems IRIS used throughout the class library to indicate success or failure of operations. Methods like %Save(), %New(), %Open(), and many others return %Status values. Understanding how to test, display, and create status values is essential.

Testing Status Values

Include %occStatus

ClassMethod TestStatus()
{
    Set obj = ##class(Sample.Person).%New()
    Set obj.Name = "Test Person"
    Set sc = obj.%Save()

    // Test if the operation succeeded
    If $$$ISOK(sc) {
        Write "Save succeeded, ID: ", obj.%Id(), !
    }

    // Test if the operation failed
    If $$$ISERR(sc) {
        Write "Save failed!", !
    }

    // IMPORTANT: Never compare %Status directly to 1 or 0
    // WRONG: If sc = 1 { ... }
    // RIGHT: If $$$ISOK(sc) { ... }
}

Displaying and Extracting Error Text

Include %occStatus

ClassMethod DisplayStatusExamples()
{
    // Create an error status for demonstration
    Set sc = $$$ERROR($$$GeneralError, "Connection timeout after 30 seconds")

    // Method 1: Display to current device
    Do $SYSTEM.Status.DisplayError(sc)
    // Output: ERROR #5001: Connection timeout after 30 seconds

    // Method 2: Get as a string (for logging, display elsewhere)
    Set text = $SYSTEM.Status.GetErrorText(sc)
    Write "Error: ", text, !

    // Method 3: Compound status with multiple errors
    Set sc1 = $$$ERROR($$$GeneralError, "First error")
    Set sc2 = $$$ERROR($$$GeneralError, "Second error")
    Set combined = $SYSTEM.Status.AppendStatus(sc1, sc2)

    // Get individual error messages
    Set first = $SYSTEM.Status.GetOneStatusText(combined, 1)
    Set second = $SYSTEM.Status.GetOneStatusText(combined, 2)
    Write "Error 1: ", first, !
    Write "Error 2: ", second, !

    // Get count of errors
    Set count = $SYSTEM.Status.GetErrorCodes(combined)
    Write "Total errors: ", count, !
}

Creating Error Status Values

Include (%occStatus, %occErrors)

ClassMethod CreateErrors()
{
    // General error with custom message
    Set sc = $$$ERROR($$$GeneralError, "Custom error message")

    // Error with specific error code
    Set sc = $$$ERROR($$$InvalidArgument, "paramName")

    // Compound status: append multiple errors
    Set sc = $$$OK
    If 'condition1 {
        Set sc = $SYSTEM.Status.AppendStatus(sc,
            $$$ERROR($$$GeneralError, "Condition 1 failed"))
    }
    If 'condition2 {
        Set sc = $SYSTEM.Status.AppendStatus(sc,
            $$$ERROR($$$GeneralError, "Condition 2 failed"))
    }

    Return sc  // Contains all accumulated errors
}

Common Pattern: Checking Return Status

Include %occStatus

ClassMethod RobustSave() As %Status
{
    Set sc = $$$OK

    Set obj = ##class(MyApp.Data).%New()
    Set obj.Field1 = "value1"

    Set sc = obj.%Save()
    If $$$ISERR(sc) {
        // Log and return
        Do $SYSTEM.Status.DisplayError(sc)
        Return sc
    }

    // Continue with more operations
    Set sc = ..AdditionalProcessing(obj.%Id())
    If $$$ISERR(sc) Return sc

    Return $$$OK
}

6. Statuses vs exceptions

Key Points

  • %Status pattern: Methods return %Status, caller checks with $$$ISERR/$$$ISOK -- used by %Save, %New, %Open
  • Exception pattern: Errors thrown as exceptions, caught by TRY-CATCH -- used for system errors and explicit THROW
  • $$$ThrowOnError(sc): Converts a %Status error into an exception (bridge from status to exception world)
  • ex.AsStatus(): Converts an exception back to a %Status value (bridge from exception to status world)
  • %Exception.StatusException: Exception subclass created from a %Status value
  • Mixing patterns: Common to use exceptions internally but return %Status to callers

Detailed Notes

Overview

InterSystems IRIS uses two distinct error signaling patterns: %Status return values and exceptions. The %Status pattern is pervasive in the class library (e.g., %Save(), %Open()), while exceptions are used for runtime errors and modern structured error handling. Real-world code must bridge both patterns fluently.

The %Status Pattern

Include %occStatus

/// Pure %Status style - check every return value
ClassMethod StatusStyle() As %Status
{
    Set sc = $$$OK

    Set obj = ##class(MyApp.Person).%New()
    Set obj.Name = "Test"

    // %Save returns %Status
    Set sc = obj.%Save()
    If $$$ISERR(sc) {
        Do $SYSTEM.Status.DisplayError(sc)
        Return sc
    }

    // %OpenId returns object or "" (with status in second arg)
    Set obj2 = ##class(MyApp.Person).%OpenId(999, , .sc)
    If $$$ISERR(sc) {
        Return sc
    }

    Return $$$OK
}

The Exception Pattern

/// Pure exception style - everything in TRY-CATCH
ClassMethod ExceptionStyle()
{
    TRY {
        Set x = 100 / 0           // <DIVIDE> - system exception
        Set ^data(1) = "value"    // <PROTECT> - system exception
    }
    CATCH ex {
        Write "Caught: ", ex.DisplayString(), !
    }
}

Converting %Status to Exception with $$$ThrowOnError

Include %occStatus

/// Recommended hybrid: Use exceptions internally, return %Status
ClassMethod RecommendedPattern() As %Status
{
    TRY {
        Set obj = ##class(MyApp.Person).%New()
        Set obj.Name = "Test"

        // %Save returns %Status -- convert to exception if error
        Set sc = obj.%Save()
        $$$ThrowOnError(sc)

        // %OpenId with status -- convert to exception if error
        Set obj2 = ##class(MyApp.Person).%OpenId(42, , .sc)
        $$$ThrowOnError(sc)

        // Now both system errors AND status errors are
        // caught by the same CATCH block
        Set result = obj2.Name _ " processed"

        Return $$$OK
    }
    CATCH ex {
        // Single error handling point
        // Convert exception back to %Status for the caller
        Return ex.AsStatus()
    }
}

Converting Exception to %Status

Include %occStatus

ClassMethod ExceptionToStatus() As %Status
{
    TRY {
        // Some operation that may throw
        Do ..RiskyOperation()
        Return $$$OK
    }
    CATCH ex {
        // ex.AsStatus() converts any exception to %Status
        Return ex.AsStatus()
    }
}

Creating Exception from %Status (Explicitly)

Include %occStatus

ClassMethod CreateStatusException()
{
    Set sc = $$$ERROR($$$GeneralError, "Something failed")

    // Create an exception from a status (without throwing)
    Set ex = ##class(%Exception.StatusException).CreateFromStatus(sc)

    // Now you have an exception object you can inspect
    Write "Name: ", ex.Name, !
    Write "Code: ", ex.Code, !
    Write "Display: ", ex.DisplayString(), !

    // Or throw it
    // THROW ex
}

Side-by-Side Comparison

Include %occStatus

/// Side-by-side: same logic, different patterns
ClassMethod ComparePatterns()
{
    // ===== STATUS PATTERN =====
    Set sc = ..DoWork1()
    If $$$ISERR(sc) {
        Set sc2 = ..DoCleanup()
        If $$$ISERR(sc2) {
            // Compound error - append
            Set sc = $SYSTEM.Status.AppendStatus(sc, sc2)
        }
        Do $SYSTEM.Status.DisplayError(sc)
        Quit
    }

    // ===== EXCEPTION PATTERN =====
    TRY {
        Do ..DoWork2()
    }
    CATCH ex {
        TRY {
            Do ..DoCleanup()
        }
        CATCH cleanupEx {
            // Log cleanup failure too
        }
        Write ex.DisplayString(), !
    }
}

The Standard Method Template

Include %occStatus

/// This is the standard recommended pattern for methods
/// that need to return %Status
ClassMethod StandardTemplate(input As %String) As %Status
{
    TRY {
        // Validate input
        If input = "" {
            $$$ThrowStatus($$$ERROR($$$GeneralError, "Input required"))
        }

        // Call methods that return %Status
        Set sc = ..Step1(input)
        $$$ThrowOnError(sc)

        Set sc = ..Step2(input)
        $$$ThrowOnError(sc)

        // Any system error (<UNDEFINED>, <SUBSCRIPT>, etc.)
        // is also caught automatically

        Return $$$OK
    }
    CATCH ex {
        Return ex.AsStatus()
    }
}

Exam Preparation Summary

Critical Concepts to Master:

  1. TRY-CATCH syntax: Know that CATCH receives a single exception object of type %Exception.AbstractException
  2. Exception properties: Memorize ex.Name, ex.Code, ex.Data, ex.Location, ex.DisplayString(), ex.AsStatus()
  3. $$$ThrowOnError vs $$$ThrowStatus: ThrowOnError only throws on error; ThrowStatus always throws
  4. ^ERRORS global: Know it stores unhandled errors automatically and is viewable in Management Portal
  5. $STACK(-1): Returns stack depth; loop from 0 to $STACK(-1) with "PLACE" and "MCODE" for full trace
  6. $$$ISERR and $$$ISOK: Never compare %Status directly to 1 or 0; always use these macros
  7. Status-exception bridge: $$$ThrowOnError converts status to exception; ex.AsStatus() converts exception to status
  8. Standard template: TRY block with $$$ThrowOnError calls, single CATCH returning ex.AsStatus()

Common Exam Scenarios:

  • Given code with a missing error check, identify what happens when an operation fails
  • Choosing between $$$ThrowOnError and $$$ThrowStatus for a specific situation
  • Reading a TRY-CATCH block and determining what ex.Name or ex.Data contains for a given error
  • Identifying the correct way to convert between %Status and exceptions
  • Determining what $STACK(n, "PLACE") returns at various stack levels
  • Recognizing that $SYSTEM.Status.GetErrorText() returns a string while DisplayError() writes to device

Hands-On Practice Recommendations:

  • Write a method using the standard TRY-CATCH template with $$$ThrowOnError
  • Deliberately cause , , and errors and examine the exception object
  • Create a custom error logging method that captures $STACK trace information
  • Practice converting between %Status and exceptions in both directions
  • Create compound %Status values with AppendStatus and extract individual errors
  • Navigate to Management Portal Application Error Log and review entries
  • Use $SYSTEM.Status.GetErrorText() and DisplayError() on various error statuses
  • Implement nested TRY-CATCH blocks and observe which CATCH handles each error

Report an Issue