PASOE still makes mysterious distinction between RETURN ERROR CH-Error versus UNDO THROW NEW AppError(CH-Error). - Forum - OpenEdge Development - Progress Community

PASOE still makes mysterious distinction between RETURN ERROR CH-Error versus UNDO THROW NEW AppError(CH-Error).

 Forum

PASOE still makes mysterious distinction between RETURN ERROR CH-Error versus UNDO THROW NEW AppError(CH-Error).

This question is answered

I've noticed that PASOE is more happy with the statement, RETURN ERROR CH-Error than it is when THROW'ing the equivalent AppError.

When THROW'ng an AppError you receive this message in the PASOE agent log.


[19/01/14@12:27:39.714-0500] P-007928 T-008080 1 AS-4 -- Cannot throw or return error/stop object to remote client because object is not serializable or client does not support it. (14438)

... But  in all other respects, we seem to get the same behavior when using the RETURN ERROR statement.

I recall that the same was true with "classic" appserver.   There is additional information in the KB that confirms a difference (in classic appserver) between when an AppError is raised via "THROW" or "RETURN ERROR". See:

https://knowledgebase.progress.com/articles/Article/000045832

Does anyone know how/why PASOE makes a distinction between those two variations?  Based on my experience with structured error handling (SEH) in ABL, the two types of statements are accomplishing the same thing.  Also, given that PASOE is able to make a distinction, would there be any way for an ABL developer to make a distinction between an AppError that was raised by "RETURN ERROR" vs "UNDO, THROW".  In the code below where the comment is, should I be able to find out how the error was raised, just like PASOE is somehow able to ???

DO TRANSACTION:
      
   RUN SomeFailingProgram.p.
   
   CATCH v_AppError 
      AS Progress.Lang.AppError:
         
         /* --->  WAS THIS A RETURN ERROR OR AN UNDO, THROW??? */
         
   END CATCH.
   
END.

Any help would be appreciated.  It is not clear to me how PASOE treats errors that are being relayed back to the client application (a .Net openclient).

Thanks in advance, David

Verified Answer
  • Not mysterious.  These are different statements doing 2 different things.  RETURN ERROR <string> raises an error and sets RETURN-VALUE.  Whereas when an AppError is thrown it raises error and there is an error object available to be caught.  The error object can have a ReturnValue set in it, or not, an error message set, or not, and possibly can have other properties if it is a custom AppError.

    Having said, that, when RETURN ERROR happens, internally, the AVM usually makes an AppError automatically, setting the ReturnValue property with the string so that if there is a CATCH statement above, this can be caught like any other thrown error.  And no, you can't tell if this came from the application itself or was manufactured by the AVM.  Don't see a reason for the app to know that.

    But when RETURN ERROR happens at the top-level procedure of an AppServer and the client is a .NET client, there is no point in making an error object because there's nothing we can do with it.  As I had said in a previous post, the OpenClient product was not upgraded to be able to handle error objects, so it still uses the old mechanism (though in 12.0, Java OpenClients have been modified to get error information from a thrown error object).  So RETURN ERROR <string> works the way it always did.  But if an error is thrown, we say that we cannot do it because the mechanism for doing that is not implemented.  Pretty straight forward.

    I believe I had suggested that you log a bug: The AVM should log the actual error message, if there is one, when you get error 14438.  I.e., If we can't throw the error to the client, we should at least get the error information in the log file.  So please do that if you haven't already.

  • OK.  I forgot it worked this way.  But what is happening with the AppError case is that if the ReturnValue property is set in an AppError, when the object is thrown, the AVM sets RETURN-VALUE from that property value.  So when this is thrown out the top of an AppServer, to an OpenClient, as I said, even though you get 14438, we still return an error and we always send over the value in RETURN-VALUE, regardless of how/when it got in there.  That is why it seems to be the seem behavior.  But what's going on internally is really very different.  We are giving you the 14438 message because normally, if you throw an error object, you'd expect the whole object (or at least all of the relevant information in it, notably the error message) to be sent back to the client, and this is not happening.

All Replies
  • Yes, I've learned to live with (and maybe understand) the differences between RETURN error and THROW, but I still occasionally have problems with multi block-level ON ERROR UNDO, THROW, where none of the CATCH executes. All are shortcut'ed by RETURN ERROR.

    So when using structured error handling, my recommendation will be to avoid mixing with classic RETURN ERROR unless you know you want the RETURN ERROR behaviour.


  • >> avoid mixing with classic RETURN ERROR unless you know you want the RETURN ERROR behaviour.

    We would like to have TOTALLY avoided "RETURN ERROR" as well. But that continues to be necessary in our top-most entry-procedure. We need to translate all AppError's into RETURN ERROR right before these errors are sent to the openclient.  Or the agent logs will get very noisy.

    Interestingly, when I reported this difference between the two (back in 2014), the KB article was written and you will see that it includes the following note: "Resolution : Scheduled to be implemented in the 11.4 OpenEdge Release" ... see https://knowledgebase.progress.com/articles/Article/000045832

    I'm not sure what that ("scheduled to be implemented") means ... but I haven't noticed any changes yet, and we are several years down the road, on a totally different appserver technology.  I'm eager to see some ambitious new enhancements for PASOE (eg. allowing the full exception detail to be marshalled from the server to the .Net openclient) but I'm not holding my breath.  In the meantime Progress might consider simply allowing AppErrors to pass quietly from server to client just like the RETURN ERROR does.  I think that is the behavior which many ABL developers would expect, given the interchangability between RETURN ERROR and THROW AppError in the ABL runtime.

    It still seems a bit mysterious that the "top-most" entry-procedure decides NOT to create an AppError from the RETURN ERROR  and it behaves totally differently than other procedures.  In theory that top-most RETURN ERROR should be just as noisy in the agent logs as if we were THROW'ing AppError. 

  • These statements have a very specific and well defined behavior.  You learn what each statement does and code it the way you want it to behave.  So your statement: "So when using structured error handling, my recommendation will be to avoid mixing with classic RETURN ERROR unless you know you want the RETURN ERROR behaviour." is kind of peculiar.  Yes, of course - as I just said, you learn what the statements do first, then you code so that it does what you want!  

    And BTW - this is always what RETURN ERROR has done.  It returns first and then raises error.  So if you were in an DO ON ERROR block, it would not follow the ON ERROR directive of that block, it would return and then raise error and abide by whatever error handling was in the caller.  Not very mysterious.

    What do you mean by:  "problems with multi block-level ON ERROR UNDO, THROW, where none of the CATCH executes"?

  • The difference in possibility to suppressing errors on block level in THROW and RETURN ERROR

    BLOCK-LEVEL ON ERROR UNDO, THROW.
    RUN p IN THIS-PROCEDURE.
    PROCEDURE p PRIVATE:
       DO ON ERROR UNDO, THROW:
          DO ON ERROR UNDO, THROW:
             RETURN ERROR.
             // UNDO, THROW NEW Progress.Lang.AppError().
             CATCH e AS Progress.Lang.AppError:
                MESSAGE "Inner block level catch" VIEW-AS ALERT-BOX.
                UNDO, THROW e.
             END CATCH.
          END.
          CATCH e AS Progress.Lang.AppError:
             MESSAGE "Outer block level catch" VIEW-AS ALERT-BOX.
             // Don't re-throw
          END CATCH.
       END.
       CATCH e AS Progress.Lang.AppError:
          MESSAGE "Internal procedure level catch" VIEW-AS ALERT-BOX.
          UNDO, THROW e.
       END CATCH.
    END PROCEDURE.
    CATCH e AS Progress.Lang.AppError:
       MESSAGE "Global procedure level catch" VIEW-AS ALERT-BOX.
       UNDO, THROW e.
    END CATCH.
  • Sorry - was there a question in there?  I've already explained the difference between RETURN ERROR and THROW.  

  • No, no question, Just something that caused our transition from classic to structured error handling (including suppressing errors) not to work as we initially expected.

    (And in our case a RETURN ERROR came from an include file, so it was not directly visible in code being modified)

    A solution we sometimes used was to wrap the include inside a procedure, so RETURN ERROR was converted to AppError in the caller.

    {incldue/return-error}

    ->

    run p.

    procedure p:

    {incldue/return-error}

    end procedure.

  • I'm still confused, since as I said, RETURN ERROR hasn't changed in terms of where the error was raised.  So you just needed to know that you needed a CATCH block in the caller. Maybe you didn't realize that.  But I don't really need to understand!  Thanks.