T5.3: Diagnoses Common Errors

Knowledge Review - InterSystems ObjectScript Specialist

1. <SUBSCRIPT> errors

Key Points

  • Accessing undefined subscript: Referencing a global or local array node that does not exist
  • Out-of-range $LIST access: Using $LIST or $LISTGET with a position beyond the list length
  • Empty string as subscript: Global subscripts cannot be the empty string ""
  • Wrong number of arguments: Passing fewer arguments than expected can cause subscript errors in called code
  • $DATA and $GET: Use these to check existence before accessing subscripts
  • $LISTLENGTH: Check list length before accessing specific positions

Detailed Notes

Overview

<SUBSCRIPT> errors occur when code attempts to use an invalid subscript to reference a global or local array node, or when $LIST operations reference positions that do not exist in a list structure. These are among the most common runtime errors in ObjectScript.

Accessing Undefined Subscript Nodes

// ERROR: Accessing a subscript that doesn't exist
Kill ^Data
Set ^Data(1) = "First"
Set ^Data(3) = "Third"

// This causes <SUBSCRIPT> if ^Data(2) doesn't exist
// and you use it in certain contexts
Write ^Data(2)    // <SUBSCRIPT> error - node 2 doesn't exist

// FIX: Use $GET with a default value
Write $GET(^Data(2), "N/A"), !   // Returns "N/A" safely

// FIX: Check with $DATA first
If $DATA(^Data(2)) {
    Write ^Data(2), !
}
Else {
    Write "No data for subscript 2", !
}

Empty String as Subscript

// ERROR: Empty string is not a valid global subscript
Set key = ""
Set ^Data(key) = "value"    // <SUBSCRIPT> error

// FIX: Validate before use
ClassMethod StoreValue(key As %String, value As %String) As %Status
{
    If key = "" {
        Return $$$ERROR($$$GeneralError, "Key cannot be empty")
    }
    Set ^Data(key) = value
    Return $$$OK
}

Out-of-Range $LIST Access

// ERROR: $LIST with out-of-range position
Set myList = $LISTBUILD("Apple", "Banana", "Cherry")
// myList has 3 elements

// Accessing position 5 causes <SUBSCRIPT> error with $LIST
Write $LIST(myList, 5)    // <SUBSCRIPT> error

// FIX: Use $LISTGET (returns "" for missing positions)
Write $LISTGET(myList, 5), !          // Returns ""
Write $LISTGET(myList, 5, "N/A"), !   // Returns "N/A"

// FIX: Check length first
If $LISTLENGTH(myList) >= 5 {
    Write $LIST(myList, 5), !
}
Else {
    Write "List has only ", $LISTLENGTH(myList), " elements", !
}

$LIST vs $LISTGET Comparison

Set list = $LISTBUILD("A", "B", "C")

// $LIST - throws <SUBSCRIPT> if position invalid
// Use when you KNOW the position exists
Set first = $LIST(list, 1)      // "A" - safe
// Set fifth = $LIST(list, 5)   // <SUBSCRIPT> error!

// $LISTGET - returns default if position invalid
// Use when position may not exist
Set first = $LISTGET(list, 1)        // "A"
Set fifth = $LISTGET(list, 5)        // "" (empty string)
Set fifth = $LISTGET(list, 5, "?")   // "?" (custom default)

Wrong Number of Arguments

// ERROR: Method expects data in a subscripted array
ClassMethod ProcessItems(ByRef items)
{
    // Expects items(1), items(2), etc.
    For i = 1:1:items {
        Write items(i), !  // <SUBSCRIPT> if items(i) doesn't exist
    }
}

// Caller passes wrong structure
ClassMethod CallerBug()
{
    Set data = 3           // Set count
    Set data(1) = "Apple"
    Set data(2) = "Banana"
    // Forgot to set data(3)!
    Do ..ProcessItems(.data)  // <SUBSCRIPT> at items(3)
}

// FIX: Defensive coding with $GET
ClassMethod ProcessItemsSafe(ByRef items)
{
    For i = 1:1:$GET(items, 0) {
        Set value = $GET(items(i), "")
        If value '= "" {
            Write value, !
        }
    }
}

2. <PROTECT> errors

Key Points

  • Database read-only: Writing to a global in a read-only or mounted read-only database
  • Insufficient permissions: Process lacks the required resource privileges to access a database
  • Namespace access denied: Process not permitted to switch to or operate in a namespace
  • Role missing database resource: User's roles do not include Read-Write access to the database resource
  • System globals: Certain system globals are protected and cannot be modified by application code
  • Resolution: Check database properties, user roles, and resource allocations in Management Portal

Detailed Notes

Overview

<PROTECT> errors occur when a process attempts an operation that violates security constraints or database access permissions. These errors are security-related and require understanding of the InterSystems IRIS security model: resources, roles, and database permissions.

Writing to a Read-Only Database

// ERROR: Database is mounted read-only
// (e.g., IRISSYS or a database mounted for backup)
Set ^SomeGlobal = "value"   // <PROTECT> if database is read-only

// Checking if you can write to the current namespace:
ClassMethod CanWrite() As %Boolean
{
    TRY {
        Set testKey = "##TEST##" _ $JOB
        Set ^CacheTemp(testKey) = 1
        Kill ^CacheTemp(testKey)
        Return 1
    }
    CATCH ex {
        If ex.Name = "<PROTECT>" {
            Return 0
        }
        THROW ex
    }
}

Handling in Application Code

ClassMethod SaveData(key As %String, value As %String) As %Status
{
    TRY {
        Set ^AppData(key) = value
        Return $$$OK
    }
    CATCH ex {
        If ex.Name = "<PROTECT>" {
            // Provide a clear message to the user
            Return $$$ERROR($$$GeneralError,
                "Cannot write to database. Check that the database " _
                "is not read-only and that your account has " _
                "Read-Write access to the database resource.")
        }
        Return ex.AsStatus()
    }
}

Common Causes and Resolutions

Cause: Database is read-only
  Check: Management Portal > System Administration > Configuration >
         System Configuration > Local Databases
  Fix:   Change database from Read-Only to Read-Write

Cause: User lacks database resource permission
  Check: Management Portal > System Administration > Security >
         Users > [user] > Roles tab
  Fix:   Add a role that has Read-Write on the database resource

Cause: Application runs as UnknownUser with minimal privileges
  Check: Management Portal > System Administration > Security >
         Users > UnknownUser
  Fix:   Assign appropriate roles or configure application to
         authenticate properly

Cause: Namespace default database differs from expectation
  Check: Management Portal > System Administration > Configuration >
         System Configuration > Namespaces
  Fix:   Verify the global database mapping for the namespace

Programmatic Security Check

ClassMethod CheckDatabaseAccess(namespace As %String) As %String
{
    // Get the default database for a namespace
    Set db = ##class(%SYS.Namespace).GetGlobalDest(namespace)
    Write "Global database: ", db, !

    // Check current user's roles
    Write "Current user: ", $USERNAME, !
    Write "Current roles: ", $ROLES, !

    Return db
}

3. <FRAMESTACK> errors

Key Points

  • Infinite recursion: Most common cause; a method calls itself without a proper base case
  • Excessive call depth: Even non-recursive code can exhaust the frame stack with deeply nested call chains
  • Indirect recursion: Method A calls Method B, which calls Method A, creating an infinite loop
  • Frame stack: Fixed-size stack that stores return addresses and context for each method call
  • Base case: Every recursive method must have a condition that stops the recursion
  • Iterative alternatives: Convert deep recursion to iterative loops when possible

Detailed Notes

Overview

<FRAMESTACK> errors occur when the execution call stack overflows. Each method call, DO command, or XECUTE pushes a frame onto the stack. When the stack is exhausted (typically after thousands of nested calls), IRIS raises a <FRAMESTACK> error. The most common cause is infinite recursion.

Infinite Recursion - Direct

// ERROR: No base case - infinite recursion
ClassMethod BadFactorial(n As %Integer) As %Integer
{
    // Missing: If n <= 1 Return 1
    Return n * ..BadFactorial(n - 1)   // Never stops!
    // Calls: BadFactorial(5) -> BadFactorial(4) -> ... -> BadFactorial(-9999) -> <FRAMESTACK>
}

// FIX: Add a proper base case
ClassMethod Factorial(n As %Integer) As %Integer
{
    If n <= 1 {
        Return 1    // BASE CASE - stops recursion
    }
    Return n * ..Factorial(n - 1)
}

Infinite Recursion - Indirect

// ERROR: Indirect recursion - A calls B, B calls A
ClassMethod ProcessA(data As %String)
{
    // Some processing...
    Set data = data _ "A"
    Do ..ProcessB(data)     // Calls B
}

ClassMethod ProcessB(data As %String)
{
    // Some processing...
    Set data = data _ "B"
    Do ..ProcessA(data)     // Calls A -> infinite loop!
}

// FIX: Add termination condition
ClassMethod ProcessA(data As %String, depth As %Integer = 0)
{
    If depth > 100 {
        Write "Maximum processing depth reached", !
        Return
    }
    Set data = data _ "A"
    Do ..ProcessB(data, depth + 1)
}

ClassMethod ProcessB(data As %String, depth As %Integer = 0)
{
    If depth > 100 {
        Write "Maximum processing depth reached", !
        Return
    }
    Set data = data _ "B"
    Do ..ProcessA(data, depth + 1)
}

Converting Recursion to Iteration

// RECURSIVE version - may cause <FRAMESTACK> for deep trees
ClassMethod TraverseTree(node As %String)
{
    Quit:node=""
    Write node, !
    Set child = $ORDER(^Tree(node, ""))
    While child '= "" {
        Do ..TraverseTree(child)    // Recursive call
        Set child = $ORDER(^Tree(node, child))
    }
}

// ITERATIVE version - uses an explicit stack (no <FRAMESTACK> risk)
ClassMethod TraverseTreeIterative(startNode As %String)
{
    // Use a local array as an explicit stack
    Set stackTop = 1
    Set stack(stackTop) = startNode

    While stackTop > 0 {
        // Pop from stack
        Set node = stack(stackTop)
        Set stackTop = stackTop - 1

        If node = "" Continue

        Write node, !

        // Push children onto stack
        Set child = $ORDER(^Tree(node, ""))
        While child '= "" {
            Set stackTop = stackTop + 1
            Set stack(stackTop) = child
            Set child = $ORDER(^Tree(node, child))
        }
    }
}

Catching Errors

ClassMethod SafeRecursion(data As %String) As %Status
{
    TRY {
        Do ..DeepRecursiveProcess(data, 0)
        Return $$$OK
    }
    CATCH ex {
        If ex.Name = "<FRAMESTACK>" {
            Return $$$ERROR($$$GeneralError,
                "Operation exceeded maximum recursion depth. " _
                "Data may be too deeply nested.")
        }
        Return ex.AsStatus()
    }
}

ClassMethod DeepRecursiveProcess(data As %String, depth As %Integer)
{
    // Guard clause: prevent <FRAMESTACK> with explicit limit
    If depth > 500 {
        Throw ##class(%Exception.General).%New(
            "MaxDepth", 5001, , "Recursion depth exceeded 500")
    }
    // ... recursive processing ...
}

4. <UNDEFINED> errors

Key Points

  • Undefined local variable: Using a variable that was never assigned a value
  • Undefined global: Referencing a global node that does not exist (reading, not setting)
  • Typo in variable name: Misspelling a variable name creates a different (undefined) variable
  • Variable not initialized: Code path reaches a variable use before its assignment
  • $GET to prevent: Use $GET(variable, default) to safely access potentially undefined variables
  • $DATA to check: Use $DATA(variable) to test existence before accessing

Detailed Notes

Overview

<UNDEFINED> is the most common runtime error in ObjectScript. It occurs when code attempts to read the value of a variable (local or global) that has no value assigned. The error message includes the variable name, making it relatively easy to diagnose. Prevention relies on defensive coding practices.

Undefined Local Variable

// ERROR: Variable never assigned
ClassMethod UndefinedExample()
{
    // Write result  // <UNDEFINED> - 'result' was never set

    // Common pattern: conditional assignment
    Set inputValue = ""
    If inputValue '= "" {
        Set processed = $ZCONVERT(inputValue, "U")
    }
    // ERROR: If inputValue was "", 'processed' was never set
    Write processed   // <UNDEFINED> *processed

    // FIX: Initialize before conditional
    Set processed = ""
    If inputValue '= "" {
        Set processed = $ZCONVERT(inputValue, "U")
    }
    Write processed, !   // Safe - always has a value
}

Typos in Variable Names

// ERROR: Typo creates a different variable
ClassMethod TypoExample()
{
    Set customerName = "John Doe"

    // ... many lines later ...

    // Typo: 'custormerName' instead of 'customerName'
    Write custormerName   // <UNDEFINED> *custormerName

    // ObjectScript is case-sensitive for variable names!
    Set myValue = 42
    Write MyValue         // <UNDEFINED> *MyValue  (capital M!)
    Write myvalue         // <UNDEFINED> *myvalue  (lowercase v!)
    Write myValue, !      // Correct: 42
}

Variable Not Initialized on All Code Paths

// ERROR: Variable only set on some branches
ClassMethod ConditionalBug(type As %String) As %String
{
    If type = "A" {
        Set result = "Type A processing"
    }
    ElseIf type = "B" {
        Set result = "Type B processing"
    }
    // What if type = "C"? 'result' is never set!
    Return result    // <UNDEFINED> *result when type = "C"

    // FIX: Initialize with default before conditionals
}

ClassMethod ConditionalFixed(type As %String) As %String
{
    Set result = "Unknown type: " _ type    // Default value

    If type = "A" {
        Set result = "Type A processing"
    }
    ElseIf type = "B" {
        Set result = "Type B processing"
    }
    Return result    // Always has a value
}

Using $GET to Prevent

ClassMethod SafeAccessExamples()
{
    // $GET with local variables
    Set name = $GET(undefinedVar)           // Returns ""
    Set name = $GET(undefinedVar, "N/A")   // Returns "N/A"

    // $GET with global variables
    Set config = $GET(^Config("timeout"), 30)  // Default 30 if not set

    // $GET with object properties (when object might be "")
    Set obj = ##class(MyApp.Person).%OpenId(999)
    // If ID 999 doesn't exist, obj = ""
    // obj.Name would cause an error, but:
    If obj '= "" {
        Set name = obj.Name
    }
    Else {
        Set name = "Not found"
    }

    // $GET with multidimensional array
    Set data(1) = "first"
    Set data(3) = "third"
    // data(2) is not set
    For i = 1:1:3 {
        Write i, ": ", $GET(data(i), "(empty)"), !
    }
    // Output:
    // 1: first
    // 2: (empty)
    // 3: third
}

Using $DATA to Check Existence

ClassMethod DataCheckExamples()
{
    Set ^Config("host") = "localhost"
    Set ^Config("port") = 8080
    // ^Config("password") is not set

    // $DATA returns:
    // 0 - node does not exist
    // 1 - node has data, no children
    // 10 - node has children, no data
    // 11 - node has both data and children

    If $DATA(^Config("password")) {
        Set pw = ^Config("password")
    }
    Else {
        Write "Password not configured!", !
    }

    // Compact form using $DATA's truthiness
    Set host = $SELECT($DATA(^Config("host")):^Config("host"), 1:"127.0.0.1")
}

Undefined Global Reference

// ERROR: Reading a global that doesn't exist
Kill ^TempData
Write ^TempData(1)    // <UNDEFINED> *^TempData(1)

// FIX: Use $GET
Write $GET(^TempData(1), "not set"), !

// FIX: Use $DATA
If $DATA(^TempData(1)) {
    Write ^TempData(1), !
}

// NOTE: Setting a global never causes <UNDEFINED>
Set ^TempData(1) = "value"   // Always works (assuming no <PROTECT>)

Comprehensive Defensive Pattern

Include %occStatus

ClassMethod ProcessRecord(id As %Integer) As %Status
{
    TRY {
        // Defensive: validate inputs
        If '$DATA(id) || (id = "") {
            Return $$$ERROR($$$GeneralError, "ID is required")
        }

        // Defensive: check object exists before using
        Set obj = ##class(MyApp.Record).%OpenId(id, , .sc)
        $$$ThrowOnError(sc)
        If obj = "" {
            Return $$$ERROR($$$GeneralError, "Record not found: " _ id)
        }

        // Defensive: use $GET for optional properties
        Set category = $GET(obj.Category, "General")
        Set priority = $GET(obj.Priority, 3)

        // Defensive: use $GET for global lookups
        Set config = $GET(^AppConfig("maxRetries"), 5)

        Return $$$OK
    }
    CATCH ex {
        If ex.Name = "<UNDEFINED>" {
            Return $$$ERROR($$$GeneralError,
                "Undefined variable: " _ ex.Data _ " at " _ ex.Location)
        }
        Return ex.AsStatus()
    }
}

5. <INVALID OREF> errors

Key Points

  • Calling method on non-object: Using dot syntax on a value that is not a valid object reference (empty string, number)
  • Failed %OpenId: `%OpenId()` returns `""` when the ID doesn't exist; calling a method on that result triggers the error
  • Expired OREF: Object reference becomes invalid after the object is closed or goes out of scope
  • $ISOBJECT check: Always verify an OREF is valid with `$ISOBJECT()` before accessing properties or methods
  • %Status checking: Use the status output parameter of `%OpenId()` to detect and handle open failures
  • Common pattern: Open → check → use; never assume `%OpenId()` succeeds

Detailed Notes

Overview

<INVALID OREF> errors occur when code attempts to call a method or access a property on a value that is not a valid object reference. The most common cause is using the result of %OpenId() without checking whether it returned a valid object. If the requested ID does not exist, %OpenId() returns "", and any subsequent dot-syntax access on that value triggers <INVALID OREF>.

Failed %OpenId Leading to INVALID OREF

// ERROR: %OpenId returns "" when ID doesn't exist
ClassMethod DisplayPerson(id As %Integer)
{
    Set person = ##class(MyApp.Person).%OpenId(id)
    // If id 999 doesn't exist, person = ""
    Write person.Name, !    // <INVALID OREF> error!
}

// FIX: Check with $ISOBJECT before use
ClassMethod DisplayPersonSafe(id As %Integer) As %Status
{
    Set person = ##class(MyApp.Person).%OpenId(id)
    If '$ISOBJECT(person) {
        Write "Person not found: ", id, !
        Return $$$ERROR($$$GeneralError, "Person not found: " _ id)
    }
    Write person.Name, !
    Return $$$OK
}

Expired or Killed Object Reference

// ERROR: OREF becomes invalid after object is killed
ClassMethod OrefScopeIssue()
{
    Set obj = ##class(%ArrayOfDataTypes).%New()
    Do obj.SetAt("value1", "key1")

    // Kill the object
    Set obj = ""

    // Now obj is "" (empty string), not an OREF
    Do obj.SetAt("value2", "key2")    // <INVALID OREF> error!

    // FIX: Check before use
    If $ISOBJECT(obj) {
        Do obj.SetAt("value2", "key2")
    }
}

Using %Status Output Parameter

// BEST PRACTICE: Use the status parameter of %OpenId
ClassMethod ProcessRecord(id As %Integer) As %Status
{
    Set record = ##class(MyApp.Record).%OpenId(id, , .sc)
    If $$$ISERR(sc) {
        // Open failed - sc contains the reason
        Return sc
    }
    If '$ISOBJECT(record) {
        Return $$$ERROR($$$GeneralError, "Record not found: " _ id)
    }

    // Safe to use record
    Set record.Status = "Processed"
    Return record.%Save()
}

6. <CLASS DOES NOT EXIST> errors

Key Points

  • Misspelled class name: Typo in the class name used with `##class()` causes immediate failure
  • Missing package name: Using a short class name without the full package path
  • Class not compiled: The class exists in source but has not been compiled yet
  • Case sensitivity: Class names in ObjectScript are case-sensitive
  • Verify with %Dictionary: Use `##class(%Dictionary.ClassDefinition).%ExistsId()` to check at runtime
  • IDE auto-complete: Use Studio or VS Code with ObjectScript extensions to prevent typos

Detailed Notes

Overview

<CLASS DOES NOT EXIST> errors occur when code references a class that cannot be found in the class dictionary. This typically happens due to typos in ##class() calls, missing package prefixes, or referencing a class that has not been compiled. The error includes the class name that was not found, making diagnosis straightforward.

Typo in Class Name

// ERROR: Misspelled class name
ClassMethod CreatePerson() As %Status
{
    // Typo: "Perso" instead of "Person"
    Set person = ##class(MyApp.Perso).%New()    // <CLASS DOES NOT EXIST>

    // FIX: Correct the class name
    Set person = ##class(MyApp.Person).%New()
}

Missing Package Prefix

// ERROR: Missing the full package path
ClassMethod OpenRecord() As %Status
{
    // Missing "MyApp." package prefix
    Set obj = ##class(Person).%New()    // <CLASS DOES NOT EXIST>

    // FIX: Include the full package name
    Set obj = ##class(MyApp.Person).%New()
}

Wrong Class Path in System Calls

// ERROR: Wrong class hierarchy for system utilities
// Common mistake with SQL statistics gathering
Do $SYSTEM.SQL.Stats.GatherTableStats("MyApp.Person")
// <CLASS DOES NOT EXIST> if the correct path differs

// FIX: Use the correct class path
Do $SYSTEM.SQL.Stats.Table.GatherTableStats("MyApp.Person")

Runtime Class Existence Check

// Checking if a class exists before using it
ClassMethod SafeCreateObject(className As %String) As %RegisteredObject
{
    // Check if the class exists in the dictionary
    If '##class(%Dictionary.ClassDefinition).%ExistsId(className) {
        Write "Class does not exist: ", className, !
        Return ""
    }

    // Safe to create an instance
    Set obj = $CLASSMETHOD(className, "%New")
    Return obj
}

7. <METHOD DOES NOT EXIST> errors

Key Points

  • Misspelled method name: Typo in the method name when calling it on an object or class
  • Missing % prefix: Many system methods start with `%` (e.g., `%Save()` not `Save()`)
  • Instance vs class method: Calling an instance method as a class method or vice versa
  • Method not in class: Method exists in a different class or superclass than expected
  • $CLASSMETHOD/$METHOD: These system functions can trigger this error with wrong method names
  • Check class definition: Verify method exists and its type (instance vs class) before calling

Detailed Notes

Overview

<METHOD DOES NOT EXIST> errors occur when code attempts to call a method that is not defined in the target class (or any of its superclasses). The most common causes are typos in method names, forgetting the % prefix on system methods, and confusing instance methods with class methods. The error message includes both the method name and the class name.

Missing % Prefix on System Methods

// ERROR: Forgetting the % prefix on system methods
ClassMethod SavePerson() As %Status
{
    Set person = ##class(MyApp.Person).%New()
    Set person.Name = "John"

    // Wrong: Save() does not exist - the method is %Save()
    Set sc = person.Save()    // <METHOD DOES NOT EXIST>

    // FIX: Use the correct method name with % prefix
    Set sc = person.%Save()
    Return sc
}

Instance Method Called as Class Method

// ERROR: Calling an instance method as a class method
ClassMethod ProcessExample()
{
    // %Save is an instance method, not a class method
    Set sc = ##class(MyApp.Person).%Save()    // <METHOD DOES NOT EXIST>

    // FIX: Create or open an instance first, then call the method
    Set person = ##class(MyApp.Person).%New()
    Set person.Name = "Jane"
    Set sc = person.%Save()    // Correct: called on instance
}

Typo in Method Name

// ERROR: Misspelled method name
ClassMethod QueryExample()
{
    Set stmt = ##class(%SQL.Statement).%New()

    // Wrong: "Preapre" instead of "Prepare"
    Set sc = stmt.Preapre("SELECT Name FROM MyApp.Person")    // <METHOD DOES NOT EXIST>

    // FIX: Correct spelling
    Set sc = stmt.%Prepare("SELECT Name FROM MyApp.Person")
}

Using $CLASSMETHOD Safely

// Dynamic method dispatch - verify method exists first
ClassMethod DynamicCall(className As %String, methodName As %String) As %String
{
    // Check if the method exists in the class
    Set methodDef = className _ "||" _ methodName
    If '##class(%Dictionary.MethodDefinition).%ExistsId(methodDef) {
        Write "Method does not exist: ", className, ".", methodName, !
        Return ""
    }

    // Safe to call
    Return $CLASSMETHOD(className, methodName)
}

8. <PROPERTY DOES NOT EXIST> errors

Key Points

  • Misspelled property name: Typo in the property name when accessing via dot syntax
  • Wrong class assumption: Object is of a different class than expected, lacking the property
  • Case sensitivity: Property names are case-sensitive in ObjectScript
  • Dynamic property access: Using `$PROPERTY()` with an incorrect property name
  • Check class definition: Use `%Dictionary.PropertyDefinition` to verify property existence at runtime
  • Computed properties: Some properties may be calculated and not stored — but they still exist in the class definition

Detailed Notes

Overview

<PROPERTY DOES NOT EXIST> errors occur when code attempts to access a property that is not defined in the object's class. This is similar to <METHOD DOES NOT EXIST> but for properties. The error message includes the property name and the class name. Common causes include typos, case-sensitivity issues, and accessing a property on the wrong class.

Typo in Property Name

// ERROR: Misspelled property name
ClassMethod DisplayPerson(id As %Integer)
{
    Set person = ##class(MyApp.Person).%OpenId(id)
    If '$ISOBJECT(person) Return

    // Wrong: "Naem" instead of "Name"
    Write person.Naem, !    // <PROPERTY DOES NOT EXIST>

    // FIX: Correct the property name
    Write person.Name, !
}

Wrong Class Assumption

// ERROR: Assuming the wrong class
ClassMethod ProcessItem(item As %RegisteredObject)
{
    // Caller passes a %ArrayOfDataTypes but code assumes MyApp.Person
    Write item.Name, !    // <PROPERTY DOES NOT EXIST> if item is not a Person

    // FIX: Check the class before accessing specific properties
    If item.%IsA("MyApp.Person") {
        Write item.Name, !
    }
    Else {
        Write "Unexpected object type: ", item.%ClassName(1), !
    }
}

Dynamic Property Access with $PROPERTY

// ERROR: Wrong property name in dynamic access
ClassMethod GetPropertyValue(obj As %RegisteredObject, propName As %String) As %String
{
    // If propName is wrong, this triggers <PROPERTY DOES NOT EXIST>
    Return $PROPERTY(obj, propName)
}

// FIX: Validate property existence first
ClassMethod GetPropertyValueSafe(obj As %RegisteredObject, propName As %String) As %String
{
    Set className = obj.%ClassName(1)
    Set propDef = className _ "||" _ propName
    If '##class(%Dictionary.PropertyDefinition).%ExistsId(propDef) {
        Write "Property does not exist: ", className, ".", propName, !
        Return ""
    }
    Return $PROPERTY(obj, propName)
}

9. <SYNTAX> errors

Key Points

  • Compile-time detection: Most syntax errors are caught when the class or routine is compiled
  • Runtime with XECUTE: Syntax errors in dynamically executed code (`XECUTE`, `$XECUTE`) occur at runtime
  • Missing closing delimiters: Unmatched parentheses, braces, or quotes are common causes
  • Wrong operator: Using an invalid operator or operator in the wrong context
  • String quoting: Missing or mismatched quotes in string literals
  • Indirection: Syntax errors in `@variable` indirection expressions are detected at runtime

Detailed Notes

Overview

<SYNTAX> errors indicate that the ObjectScript parser cannot interpret the code due to a structural problem. Most syntax errors are caught at compile time, preventing the code from running at all. However, syntax errors can also occur at runtime when using dynamic code execution (XECUTE command, $XECUTE function) or indirection (@), since these construct and parse code strings at runtime.

Missing Closing Parenthesis

// ERROR: Missing closing parenthesis (caught at compile time)
ClassMethod CompileError()
{
    Set x = $LENGTH("hello"    // <SYNTAX> - missing closing )

    // FIX: Match all parentheses
    Set x = $LENGTH("hello")
}

Unbalanced Braces

// ERROR: Missing closing brace (caught at compile time)
ClassMethod BraceError()
{
    If x = 1 {
        Write "one", !
    // Missing closing }     // <SYNTAX>

    // FIX: Ensure every { has a matching }
    If x = 1 {
        Write "one", !
    }
}

Runtime Syntax Error with XECUTE

// ERROR: Syntax error in dynamic code (caught at RUNTIME)
ClassMethod DynamicCodeError()
{
    Set code = "Set x = $LENGTH(""hello"""    // Missing closing )

    // This compiles fine - the string is valid
    // But executing it causes <SYNTAX> at runtime
    XECUTE code    // <SYNTAX> error at runtime!

    // FIX: Validate dynamic code carefully
    Set code = "Set x = $LENGTH(""hello"")"
    XECUTE code    // Works correctly
}

Syntax Error with Indirection

// ERROR: Invalid expression in indirection
ClassMethod IndirectionError()
{
    Set expr = "1 + + 2"    // Invalid expression
    Set result = @expr       // <SYNTAX> at runtime

    // FIX: Validate the expression
    Set expr = "1 + 2"
    Set result = @expr       // Works: result = 3
}

Common Syntax Mistakes

// Common syntax errors and their fixes:

// 1. Missing dot before method call in same class
//    Do MyMethod()     // <SYNTAX> in instance context
//    Do ..MyMethod()   // Correct for instance methods

// 2. Wrong string quoting (doubling quotes inside strings)
//    Set x = "He said "hello""    // <SYNTAX>
//    Set x = "He said ""hello"""  // Correct

// 3. Missing comma in function arguments
//    Set x = $EXTRACT(str 1 3)    // <SYNTAX>
//    Set x = $EXTRACT(str, 1, 3)  // Correct

// 4. Using = instead of == in expressions (not an error but a logic bug)
//    Note: ObjectScript uses = for both assignment and comparison
//    depending on context, so this is valid syntax

Documentation References

Exam Preparation Summary

Critical Concepts to Master:

  1. : Know that $LIST throws for out-of-range but $LISTGET returns "" safely
  2. : Understand the relationship between databases, resources, roles, and access permissions
  3. : Recognize infinite recursion (direct and indirect) as the primary cause
  4. : Know that reading (not setting) an undefined variable causes the error, and ex.Data contains the variable name
  5. : Occurs when calling a method or property on a non-object; always check `$ISOBJECT()` after `%OpenId()`
  6. : Typos in `##class()`, missing package names, or uncompiled classes
  7. : Missing `%` prefix on system methods, instance vs class method confusion
  8. : Typos in property names, wrong class assumption, case sensitivity
  9. : Usually caught at compile time, but can occur at runtime with `XECUTE` or indirection
  10. $GET as prevention: $GET is the primary defensive tool for both and prevention
  11. $DATA for checking: $DATA returns 0 for non-existent nodes, non-zero for existing
  12. Error names in ex.Name: Know the angle-bracket format: , , , , , etc.
  13. Base cases: Every recursive method must have a condition that terminates recursion

Common Exam Scenarios:

  • Given code that accesses a global subscript, determine if a error can occur
  • Identifying the cause of a error (read-only database, missing role, resource permission)
  • Spotting infinite recursion in code and identifying the fix (add base case)
  • Recognizing that a variable used in one branch of an IF but not another can cause
  • Choosing between $GET and $DATA for a given defensive coding scenario
  • Determining which $LIST function is safe for potentially out-of-range access ($LISTGET)
  • Identifying case-sensitivity issues in variable names that lead to
  • Recognizing that `%OpenId()` can return `""` and that using dot syntax on it causes
  • Spotting typos in class names, method names, or property names that cause "DOES NOT EXIST" errors
  • Distinguishing between compile-time errors and runtime from XECUTE/indirection
  • Knowing that `Save()` is wrong but `%Save()` is correct (missing % prefix pattern)

Hands-On Practice Recommendations:

  • Deliberately cause each error type and examine the exception object
  • Write recursive methods with and without base cases to observe
  • Create code that uses $GET and $DATA defensively, then remove the guards to see the errors
  • Set up a read-only database and try writing to it to observe
  • Write a method with a typo in a variable name and observe the error with ex.Data
  • Practice converting recursive algorithms to iterative ones using explicit stacks
  • Use $LISTGET vs $LIST with out-of-range positions to understand the difference
  • Open a non-existent ID with `%OpenId()` and try accessing a property to see
  • Deliberately misspell a class name in `##class()` to see
  • Call `Save()` instead of `%Save()` to see
  • Use XECUTE with malformed code to observe runtime errors
  • Build a comprehensive error handler that identifies each error type and provides specific guidance

Report an Issue