Skip to content

Faults and error pipeline

All six services share one uniform, purely observational fault pipeline: a [LogErrorBehavior] service behavior installs an ErrorLogErrorHandler on every channel dispatcher, that handler logs the exception and returns false, and because includeExceptionDetailInFaults="true" the caller then receives the framework's default fault carrying the full exception type, message, and stack trace. No operation declares a fault contract, and the one explicit FaultException construction in the host is unreachable dead code.

Fault surface summary

Aspect Finding
Declared fault contracts None — no [FaultContract] anywhere in this host
Service behavior attributes [LogErrorBehavior] on all 6 service classes
IErrorHandler implementations ErrorLogErrorHandler (src/WCF/SAIF.Services.WCF.Host/Extensions/ErrorLogErrorHandler.cs)
includeExceptionDetailInFaults true (src/WCF/SAIF.Services.WCF.Host/Web.Base.config)
Effect on callers Every unhandled exception reaches the caller as a generic SOAP fault carrying full server-side exception detail — type, message, and stack trace

Service behaviors

Behavior Applied to Declared in Effect
[LogErrorBehavior] FileService, PreferenceService, WebContextService, ImagingService, FaxService, SecurityService src/WCF/SAIF.Services.WCF.Host/Extensions/LogErrorBehaviorAttribute.cs Installs ErrorLogErrorHandler on every channel dispatcher through ApplyDispatchBehavior
<serviceDebug includeExceptionDetailInFaults="true"/> All services Web.Base.config <serviceBehaviors> Every fault reaching a caller carries full exception detail, regardless of what ErrorLogErrorHandler does
<serviceMetadata httpGetEnabled="true"/> All services Web.Base.config <serviceBehaviors> WSDL is published per route via ?wsdl

Error handler

Field Value
Type ErrorLogErrorHandler : IErrorHandler
Source src/WCF/SAIF.Services.WCF.Host/Extensions/ErrorLogErrorHandler.cs
Installed by LogErrorBehaviorAttribute : IServiceBehavior — ApplyDispatchBehavior
Scope Every channel dispatcher on every one of the six service classes
HandleError return false — the exception is not considered handled
ProvideFault behavior Empty method body — leaves the fault unchanged; WCF produces the default fault

HandleError builds a SAIF.Core.Contracts.ApplicationExceptionDetail, hard-coding Application = "WCF" and reading the environment track, machine name, and calling user from a freshly constructed WindowsEnvironmentService, then passes it to new LogService(environmentService).LogException(exceptionDetail). It then returns false, so the framework continues to the standard fault path unchanged. Because ProvideFault has an empty body, the handler never replaces or reshapes the outgoing fault message — the pair is purely observational: it logs and never suppresses, transforms, or shapes a fault.

Logged exception detail

Field Value written Source of the value
Application "WCF" (hard-coded) Literal in ErrorLogErrorHandler.HandleError
Environment / machine / user Environment track, machine name, calling user new WindowsEnvironmentService()
Exception detail error.GetErrorLogFormattedException(), the failing method signature, and the calling user Exception, error.TargetSite
Aspect Finding
Log surface LogService (new LogService(environmentService).LogException(exceptionDetail))
Destination Not determined from source — LogService is supplied by the external framework closure
Failure of the logger itself HandleError dereferences error.TargetSite.DeclaringType with no null guard; TargetSite is null for some exception shapes, in which case logging itself would throw inside the error handler (see Tech debt)

Exception messages originating from FileService and PreferenceService can contain caller-supplied path and user values, so those values reach this central exception log alongside the calling user identity.

Error pipeline diagram

The following diagram visualizes how an exception becomes a fault.

flowchart TD
    OP["Operation throws an exception"] --> DISP["Channel dispatcher"]
    DISP --> HANDLER["ErrorLogErrorHandler.HandleError"]
    HANDLER --> LOG["Exception detail logged<br>via LogService"]
    HANDLER --> RET["HandleError returns false"]
    RET --> DEFAULT["WCF produces the default fault"]
    DISP --> PROVIDE["ErrorLogErrorHandler.ProvideFault<br>empty body, fault unchanged"]
    PROVIDE --> DEFAULT
    DEFAULT --> DETAIL["includeExceptionDetailInFaults=true<br>type, message, stack trace included"]
    DETAIL --> CALLER["Caller receives SOAP fault<br>with full exception detail"]

Declared fault contracts

No operation in this host declares a fault contract. Every failure reaches the caller as a generic SOAP fault whose content is determined by the error pipeline described above. The one explicit FaultException construction in the host — ImagingService.ThrowTimeoutException(int), building a FaultCode("RequestTimeout") — is private, has no call site anywhere in the host, and is unreachable dead code; no caller ever receives it (see Tech debt).

Failure modes by operation

Operation Failure What the caller receives Detail exposed
ReadFile Unhandled exception in the operation body Generic SOAP fault Type, message, and stack trace (includeExceptionDetailInFaults="true")
ReadEntireFile Unhandled exception in the operation body Generic SOAP fault Type, message, and stack trace (includeExceptionDetailInFaults="true")
WriteFile Unhandled exception in the operation body Generic SOAP fault Type, message, and stack trace (includeExceptionDetailInFaults="true")
DeleteFile Unhandled exception in the operation body Generic SOAP fault Type, message, and stack trace (includeExceptionDetailInFaults="true")
Length Unhandled exception in the operation body Generic SOAP fault Type, message, and stack trace (includeExceptionDetailInFaults="true")
GetPreferences Unhandled exception in the operation body Generic SOAP fault Type, message, and stack trace (includeExceptionDetailInFaults="true")
SavePreferences Unhandled exception in the operation body Generic SOAP fault Type, message, and stack trace (includeExceptionDetailInFaults="true")
GetDefaultPrinter Unhandled exception in the operation body Generic SOAP fault Type, message, and stack trace (includeExceptionDetailInFaults="true")
Get Unhandled exception in the operation body Generic SOAP fault Type, message, and stack trace (includeExceptionDetailInFaults="true")
Save Unhandled exception in the operation body Generic SOAP fault Type, message, and stack trace (includeExceptionDetailInFaults="true")
CreateContext Unhandled exception in the operation body Generic SOAP fault Type, message, and stack trace (includeExceptionDetailInFaults="true")
QueueImage Unhandled exception in the operation body Generic SOAP fault Type, message, and stack trace (includeExceptionDetailInFaults="true")
ReserveImageId Unhandled exception in the operation body Generic SOAP fault Type, message, and stack trace (includeExceptionDetailInFaults="true")
SendFax Unhandled exception in the operation body Generic SOAP fault Type, message, and stack trace (includeExceptionDetailInFaults="true")
IsAuthorized Unhandled exception in the operation body Generic SOAP fault Type, message, and stack trace (includeExceptionDetailInFaults="true")
IsAuthorized2 Unhandled exception in the operation body Generic SOAP fault Type, message, and stack trace (includeExceptionDetailInFaults="true")
GetUser Unhandled exception in the operation body Generic SOAP fault Type, message, and stack trace (includeExceptionDetailInFaults="true")
GetSaifUserByUserId Unhandled exception in the operation body Generic SOAP fault Type, message, and stack trace (includeExceptionDetailInFaults="true")
GetSaifUserByEmployeeId Unhandled exception in the operation body Generic SOAP fault Type, message, and stack trace (includeExceptionDetailInFaults="true")
GetUserIdForEmployeeId Unhandled exception in the operation body Generic SOAP fault Type, message, and stack trace (includeExceptionDetailInFaults="true")
GetExternalUser Unhandled exception in the operation body Generic SOAP fault Type, message, and stack trace (includeExceptionDetailInFaults="true")
GetExternalUserByCustomerId Unhandled exception in the operation body Generic SOAP fault Type, message, and stack trace (includeExceptionDetailInFaults="true")
GetInjuredWorkerUserByCustomerId Unhandled exception in the operation body Generic SOAP fault Type, message, and stack trace (includeExceptionDetailInFaults="true")
GetUsersByGroup Unhandled exception in the operation body Generic SOAP fault Type, message, and stack trace (includeExceptionDetailInFaults="true")

Swallowed failures

Location Behavior Consequence
SecurityService.LogRequestDetails (src/WCF/SAIF.Services.WCF.Host/Services/Security/SecurityService.cs) Writes a JSON fragment of the operation name and arguments to the IIS log via HttpContext.Current.Response.AppendToLog(msg), wrapped so a logging failure does not fault the caller A logging failure is invisible to both the caller and the operator; the intended LOG_REQUEST_DETAILS kill switch is never consulted either (see Tech debt)
SecurityService constructor appSettings reads (src/WCF/SAIF.Services.WCF.Host/Services/Security/SecurityService.cs) ConfigurationManager.AppSettings reads for LOG_REQUEST_DETAILS and LDAP_EXTERNAL_KEY are each wrapped in an empty try/catch A missing or misconfigured key fails silently; ldap_external_key stays null and the external LDAP host silently falls back to a literal default

Sources

  • src/WCF/SAIF.Services.WCF.Host/Extensions/ErrorLogErrorHandler.cs
  • src/WCF/SAIF.Services.WCF.Host/Extensions/LogErrorBehaviorAttribute.cs
  • src/WCF/SAIF.Services.WCF.Host/Web.Base.config
  • src/WCF/SAIF.Services.WCF.Host/Services/ImagingService.cs
  • src/WCF/SAIF.Services.WCF.Host/Services/Security/SecurityService.cs