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¶
- Add a message for the top-level exception.
ProcessException(ex As Exception)first callsAddEnvironmentMessage(ex.Message, ex.GetType.Name), which constructs aNew ServiceMessage(inKey:=ex.GetType.Name, inMessage:=ex.Message, inType:=ServiceMessageType.Environment)and appends it to the private_Messageslist. - Capture the inner-exception reference. A local variable
LocalEXis set toex.InnerException. - Loop while an inner exception reference is present.
Do While LocalEX IsNot Nothing: - Add another environment message for the current
LocalEX:AddEnvironmentMessage(LocalEX.Message, LocalEX.GetType.Name). - Reassign the loop variable:
LocalEX = ex.InnerException. - Defect: step 3.2 reassigns
LocalEXfromex.InnerException(the original top-level exception's inner exception) rather than fromLocalEX.InnerException(the current exception's inner exception). Ifex.InnerExceptionis notNothing,LocalEXis set to the exact same non-Nothingreference on every iteration, so the loop conditionLocalEX IsNot Nothingnever becomes false and the loop never terminates — it repeatedly adds the same inner-exception message to_Messagesforever. This is a direct read of the source, not an inference: nested exceptions (any exception with a non-NothingInnerException) passed toProcessExceptionwill hang the calling thread and grow_Messageswithout bound until the process is terminated or runs out of memory. - Only an exception whose
InnerExceptionisNothing(i.e., no inner exception at all) avoids the loop entirely, becauseLocalEXstarts atNothingand theDo Whilecondition is false on the first check. - Callers add other message types independently. Outside of
ProcessException, callers can also addError,Warning, orInformationmessages directly viaAddErrorMessage(...),AddWarningMessage(...), andAddInformationMessage(...), each constructing aServiceMessageof the matchingServiceMessageTypeand appending it to the same_Messageslist. - Consumers filter by message type. A consumer of the
ServiceResponsereadsErrors,Warnings,InformationalMessages, orEnvironmentalErrors— each aLINQ Wherefilter overMessagesbyServiceMessageType— to react to the specific category of message it cares about.Messagesitself exposes the full, unfiltered_Messageslist. - Overall result flag is independent of messages.
ServiceResponse.Result(Boolean, defaultsTrue) andRequestIDare plain public fields set directly by the consuming service code;ProcessExceptionand theAdd*Messagemethods do not themselves setResulttoFalse— 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-
NothingInnerException. As described in step 3,ProcessExceptionnever 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 duplicateServiceMessageentries to_Messages. This affects any caller — service method, batch process, or web endpoint — that callsProcessExceptionon a caught exception carrying an inner exception (a very common .NET pattern for wrapped exceptions). - Only exceptions with no
InnerExceptionare processed correctly and return promptly with exactly one added message. - Because the defect is inside
ServiceResponseitself (not a subclass override), every consumer ofServiceResponse.ProcessExceptionin the codebase is exposed to this behavior; there is no workaround available to callers other than avoidingProcessExceptionfor exceptions known to carry inner exceptions. AddEnvironmentMessage,AddErrorMessage,AddWarningMessage, andAddInformationMessageall accept an optionalinKeythat defaults to an empty string;ProcessExceptionalways suppliesex.GetType.Nameas the key, so environment messages can be distinguished by exception type throughServiceMessage.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.
Related documentation¶
Sources¶
Application\SAIF.Application\Service Based\ServiceResponse.vbApplication\SAIF.Application\Service Based\ServiceMessage.vbApplication\SAIF.Application\Service Based\ServiceEnums.vb