Question
· Sep 20, 2023

Access Ens.MessageHeader on Error in Business Service

When making a synchronous request to a custom business process and there's an error and  0 is returned, it seems that the response is not sent back to the custom business service.  Rather, a Ens.MessageHeader with IsError=1 is returned without a message body (which would normally be the response object).

I tried using ##class(Ens.MessageHeader).%OpenId($$$JobCurrentHeaderId).IsError to check the error status but I keep getting the node that $$$JobCurrentHeaderId compiles to is undefined.  There's clearly a Ens.MessageHeader object being sent back to the process, but I can't access it.  My solution is to check $ISOBJECT(pResponse) and if 0 quit 0 in the method so it takes me to OnError() where I can do some custom code.

Is there a way?

For context, we are doing something a bit unconventional here: normally the business process would error and we would handle the error there and maybe disable the business process until we can fix the problem.  In this case we want the business process to keep handling other requests as they come in as they all execute different code, but the service should be disabled because it's that request's path that is causing the error, not the business process.  

Product version: IRIS 2023.1
$ZV: IRIS for UNIX (Ubuntu Server LTS for ARM64 Containers) 2023.1 (Build 229U) Fri Apr 14 2023 17:20:01 EDT
Discussion (2)2
Log in or sign up to continue

When an error has occurred, the return value is not 0. Instead, it is a %Status, and likely already contains the error information you need.

As to your actual question, finding (the id of) the response message header is not trivial. Ensemble Business Services have a %RequestHeader property from which you can take the CorrespondingMessageId, but unfortunately it is only populated if the call did not return an error status. It is still possible, though. You can use the id of the request body to find out what you need. It does need some SQL, though:

ClassMethod GetResponseHeaderId(RequestId As %String, Output sc As %Status) As %String
{
  &sql(SELECT CorrespondingMessageId INTO :ResponseHeaderId
         FROM Ens.MessageHeader
        WHERE MessageBodyId = :RequestId)
  If SQLCODE Set sc = $$$ERROR($$$SQLError, SQLCODE, $Get(%msg))
  
  Set sc = $$$OK
  
  Return ResponseHeaderId
}

With this helper method, you can open the response message header object. Assuming the request body is called CallReq:

  Set Id = ..GetResponseHeaderId(CallReq.%Id(), .sc)
  If 'sc Quit sc
  
  Set RspHdr = ##class(Ens.MessageHeader).%OpenId(Id, , .sc)
  If 'sc Quit sc
  
  $$$LOGINFO("Response is an error: "_RspHdr.IsError)

HTH,
Gertjan.

@Gertjan Klein thanks for your help and sorry for my delayed response.

I did not realize the message body is not set if there’s an error.  This explains a lot.  I was previously doing a try/catch and trying to set the %Status error text into the StringContainter response and frustrated that it wasn’t there.  I think I’m trying to overdo what Ensemble already does for free.  I also think I need to keep a response a response and keep errors as errors and not try to combine them. 

But thank you for your method here to get the header ID.  Since as you say it's not trivial, it makes me think it's unconventional and again I'm trying to use the system in ways it wasn't really designed for.  However, it's handy to understand how this works.  Thanks for helping me think through this!