Article
· Dec 5, 2024 1m read

How to raise a custom error

InterSystems FAQ rubric

If you want to raise an arbitrary custom error in a TRY block, you can pass an exception with a throw as follows. In the following example, a custom error is raised if Stcount is less than 1.

Class User.Test
{

ClassMethod ExceptionTest()
 {
    try
    {
      // : some codes
      if (Stcount<1) {
          throw ##class(%Exception.General).%New("User-defined error", "5001", "location", "Data at location error")
          // User-created errors are 5001 and above
      }
    }
    catch ex
    {
      write "Errors #", ex.Code, ": ", ex.Name, " : ", ex.Location, " ", ex.Data
      return
    }
 }
}

In the above example, if Stcount is less than 1, an error like the following will be output:

USER>do ##class(User.Test).ExceptionTest()
Error #5001: User-defined error: Data at location error

For more information, see the following documentation:
ObjectScript command _THROW

If you want to create an arbitrary status code, do the following:

USER>set st = ##class(%SYSTEM.Status).Error(5001,"This is a user-defined error")

USER>zwrite st
st="0 "_$lb($lb(5001,"This is a user-defined error",,,,,,,,$lb(,"USER",$lb("e^zError+1^%SYSTEM.Status.1^1","e^^^0"))))/* Error #5001: This is a user-defined error */
USER>do $SYSTEM.Status.DisplayError(st)

Error #5001: This is a user-defined error
Discussion (1)1
Log in or sign up to continue

Throwing custom errors is a really handy tool to have!

You can also take this a step further and if you have code that returns a status (either built in functions such as %Save() or custom errors like you have here), you can deconstruct the status object to get into a nice output string using $System.Status.DecomposeError(). This is especially useful because functions like %Save() don't actually throw the errors again to the caller, they just return the status code from the error thrown within %Save(), so a try/catch won't catch these.

Take, for instance, the following test class. Notice the property on the class is of type %Integer, but in the class method I attempt to set the value to a string. This will cause an error when using %Save():

Class ErrorTest extends %Persistent {
    Property TestProp As %Integer;

    ClassMethod ErrorTest()
    {
      set objectReference = ..%New()
      set objectReference.TestProp = "Test"
      set status = objectReference.%Save()
      if status '= 1 {
        do $System.Status.DecomposeStatus(status,.errorlist)
        for errornum = 1:1:errorlist {
          write !,"Error entry ",errornum,":"
          write !,errorlist(errornum)
        }
      }
    }
}

If I were to wrap the %Save() call in a try/catch, it wouldn't catch the error thrown by the invalid property type when %Save is called. Using the method above, when I run the code I get this:

USER> do ##class(Custom.Robbie.ErrDecomposeTest).ErrorTest()

Error entry 1:
ERROR #7207: Datatype value 'Test' is not a valid number
> ERROR #5802: Datatype validation failed on property 'Custom.Robbie.ErrDecomposeTest:TestProp', with value equal to "Test"


So, when you're using any kind of status, whether it be errors statuses returned by built in functions or custom made error statuses, you can use this to decompose them and get at the various pieces within if the error status isn't actually thrown to be caught by a try/catch.