Skip to content

Exception-to-message conversion

ServiceResponse is the service-oriented result container used by clsServiceAppManagerBase-based managers and other service callers to report success/failure and a typed list of ServiceMessage entries back to a consumer. ProcessException(ex) is the conversion entry point that turns a caught .NET exception into one or more Environment-typed messages.

Field Value
Operation ID OP-APP-07
Trigger Service-oriented code catches an exception and calls response.ProcessException(ex) on a ServiceResponse instance
Inputs The caught Exception instance (and, intended, its InnerException chain)
Outputs One or more ServiceMessage entries of type ServiceMessageType.Environment added to the ServiceResponse's internal message list, surfaced through the EnvironmentalErrors read-only property
Primary components ServiceResponse.ProcessException, ServiceResponse.AddEnvironmentMessage, ServiceMessage.New, ServiceResponse.EnvironmentalErrors, ServiceResponse.Errors, ServiceResponse.Warnings, ServiceResponse.InformationalMessages
Entry point Application\SAIF.Application\Service Based\ServiceResponse.vb

Steps

  1. Add a message for the top-level exception. ProcessException(ex As Exception) first calls AddEnvironmentMessage(ex.Message, ex.GetType.Name), which constructs a New ServiceMessage(inKey:=ex.GetType.Name, inMessage:=ex.Message, inType:=ServiceMessageType.Environment) and appends it to the private _Messages list.
  2. Capture the inner-exception reference. A local variable LocalEX is set to ex.InnerException.
  3. Loop while an inner exception reference is present. Do While LocalEX IsNot Nothing:
  4. Add another environment message for the current LocalEX: AddEnvironmentMessage(LocalEX.Message, LocalEX.GetType.Name).
  5. Reassign the loop variable: LocalEX = ex.InnerException.
  6. Defect: step 3.2 reassigns LocalEX from ex.InnerException (the original top-level exception's inner exception) rather than from LocalEX.InnerException (the current exception's inner exception). If ex.InnerException is not Nothing, LocalEX is set to the exact same non-Nothing reference on every iteration, so the loop condition LocalEX IsNot Nothing never becomes false and the loop never terminates — it repeatedly adds the same inner-exception message to _Messages forever. This is a direct read of the source, not an inference: nested exceptions (any exception with a non-Nothing InnerException) passed to ProcessException will hang the calling thread and grow _Messages without bound until the process is terminated or runs out of memory.
  7. Only an exception whose InnerException is Nothing (i.e., no inner exception at all) avoids the loop entirely, because LocalEX starts at Nothing and the Do While condition is false on the first check.
  8. Callers add other message types independently. Outside of ProcessException, callers can also add Error, Warning, or Information messages directly via AddErrorMessage(...), AddWarningMessage(...), and AddInformationMessage(...), each constructing a ServiceMessage of the matching ServiceMessageType and appending it to the same _Messages list.
  9. Consumers filter by message type. A consumer of the ServiceResponse reads Errors, Warnings, InformationalMessages, or EnvironmentalErrors — each a LINQ Where filter over Messages by ServiceMessageType — to react to the specific category of message it cares about. Messages itself exposes the full, unfiltered _Messages list.
  10. Overall result flag is independent of messages. ServiceResponse.Result (Boolean, defaults True) and RequestID are plain public fields set directly by the consuming service code; ProcessException and the Add*Message methods do not themselves set Result to False — a caller must explicitly do so when handling a failure.*

Process flow

The following diagram visualizes the exception-processing sequence, including the non-terminating loop defect for nested exceptions.

flowchart TD
    Start([ProcessException ex called]) --> AddTop[Add Environment message<br>for ex.Message]
    AddTop --> SetLocal[LocalEX = ex.InnerException]
    SetLocal --> Check{LocalEX IsNot Nothing?}
    Check -->|No inner exception| Done([Return: one message added])
    Check -->|Has inner exception| AddInner[Add Environment message<br>for LocalEX.Message]
    AddInner --> Reassign[BUG: LocalEX reassigned to<br>ex.InnerException, not LocalEX.InnerException]
    Reassign --> Check

Failure and edge-case behavior

  • Infinite loop on any exception with a non-Nothing InnerException. As described in step 3, ProcessException never advances past the original exception's immediate inner exception, so any exception with at least one inner exception causes the loop to run forever, continuously appending duplicate ServiceMessage entries to _Messages. This affects any caller — service method, batch process, or web endpoint — that calls ProcessException on a caught exception carrying an inner exception (a very common .NET pattern for wrapped exceptions).
  • Only exceptions with no InnerException are processed correctly and return promptly with exactly one added message.
  • Because the defect is inside ServiceResponse itself (not a subclass override), every consumer of ServiceResponse.ProcessException in the codebase is exposed to this behavior; there is no workaround available to callers other than avoiding ProcessException for exceptions known to carry inner exceptions.
  • AddEnvironmentMessage, AddErrorMessage, AddWarningMessage, and AddInformationMessage all accept an optional inKey that defaults to an empty string; ProcessException always supplies ex.GetType.Name as the key, so environment messages can be distinguished by exception type through ServiceMessage.Key, whereas manually-added messages of other types may have an empty key unless the caller supplies one.*

Note: Statements marked with * were inferred from code structure, naming conventions, or contextual evidence where direct confirmation was not available in source artifacts.

Sources

  • Application\SAIF.Application\Service Based\ServiceResponse.vb
  • Application\SAIF.Application\Service Based\ServiceMessage.vb
  • Application\SAIF.Application\Service Based\ServiceEnums.vb