Error reporting
The way WS API services report errors has changed in version 5.0.23. From this point memoQ Server now supports two different modes:
- Legacy mode. This is the error handling mode memoQ Server supported in versions less than 5.0.23. Using this error mode there is no way to specifically handle different errors: all errors are grouped into expected and unexpected category, and only this category is returned to the caller. This error mode is kept only to preserve compatibility with previous versions, and may be eliminated in a future version.
- Advanced fault handing mode. This new error handling mode is supported by memoQ Server version 5.0.23 and above. For most part of the API works the same way as legacy mode (errors are grouped into expected and unexpected categories, and only the category is returned to the caller). But a subset of the API (TM lookup, ELM) enables the caller to differentiate between individual errors (and not only their category). This is the recommended mode for new development.
Error handling configuration
The error handling mode is set in the ProgramData\MemoQ Server\WSIFConfig.xml configuration file. To enable advanced error handling set the value of the AdvancedFaultHandling element to true. If this value is false (or the element is missing), the error handling mode is Legacy mode.
<?xml version="1.0" encoding="utf-8"?>
<WsIfConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
...
<AdvancedFaultHandling>true</AdvancedFaultHandling>
</WsIfConfig>
There is an easier way to set the error handling mode using memoQ Client. Open Server Administrator, connect to memoQ Server, select the "Web Service interface" category, and check the "Use advanced fault handling" checkbox to enable advanced error handling (opposed to legacy mode).
The default error handling mode for clean installations is "Advanced fault handing mode". However, the error handling mode is "Legacy mode" when upgrading from previous versions.
Exception categories
Errors can be grouped into the following two categories.
- Expected errors/warnings: These can happen under normal conditions. A few examples: trying to import into a read only TM, trying to modify a deleted project, etc. The text of the fault is a message that can be displayed to the user.
- Unexpected errors: The origin of these errors can be:
- The incorrect use of the API (e.g., missing or incorrect parameters). Such errors are only expected during the development and testing phase of the module's deployment.
- Unexpected errors during normal operation. Such errors are unlikely; if they do occur, the project manager/system administrator needs to be notified. The cause of these problems can be network errors, inaccessible database, an unexpected failure in memoQ server, etc. If the problem cannot be solved locally, detailed report should be sent to Kilgray support.
An error message something like "Internal memoQ Server error" should be displayed to the user. The information contained in the exception (fault) should not be displayed to the user. Instead, the log of the memoQ Server should be used to determine the cause of the error.
Technology
Errors are reported as SOAP exceptions/faults using most client technologies. The SOAP standard defines the structure of SOAP fault messages. There are two SOAP standards (1.1 and 1.2), the structure of fault messages is slightly different for these. Luckily, most parts are the same, and memoQ Server utilizes only these parts:
- Fault code: A string code identifying the cause of the error.
- Message: A textual description of the problem
- Detail: Optional. Place to send extra structured information with the fault. This can be accesses using XML parser using old WS technologies, but modern tools generate classes/object (in the languages used) based on the fault contracts defined in WSDL, so it is possible to access this information in a typed manner. Certain technologies also enable to catch faults based on their fault type (which is defined in fault contracts).
The error handling – both in case of legacy and advanced mode - has been elaborated to support different platforms (ASMX web services, WCF web services, Java, etc.)
Legacy mode
Using this error mode there is no way to specifically handle different errors: all errors are grouped into expected and unexpected category, and only this category is returned to the caller.
Different web service platforms handle SOAP faults slightly differently (but typically an exception is thrown). This exception holds the fault code. The value of this fault code can be used to differentiate between expected/unexpected errors: for expected errors, the fault code name is "Server.Expected", for unexpected error it is a different string.
An example showing how to differentiate between expected/unexpected errors in WCF:
try
{
// service call
}
catch (FaultException ex)
{
if (ex.Code.Name == "Server.Expected")
MessageBox.Show("Expected exception. The following message can be displayed to the user: \n\n" + ex.Message);
else
MessageBox.Show("Unexpected exception. See server log for details.");
}
An example showing how to differentiate between expected/unexpected errors in an ASMX client:
try
{
// service call
}
catch (SoapException ex)
{
if (ex.Code.Name == "Server.Expected")
MessageBox.Show("Expected exception. The following message can be displayed to the user: \n\n" ex.Message);
else
MessageBox.Show("Unexpected exception. See memoQ server log for details.\nMessage: " + ex.Message);
}
It should be noted, that memoQ Server logs all exceptions, and this log can be used to check for the internal details of the exceptions.
Advanced error handling mode
Note : Although the LookupSegment operation is used for demonstration, this operation is considered to be deprecated, and will be eliminated in 8.0. But the error handling concept/technique is general, also stands in other contexts.
This error handling mode also enables the caller to differentiate between expected and unexpected errors (though in a way different from the way legacy mode works). However, for a subset of the API, this mode also enables to differentiate between individual errors (not only their category), and this way enables the caller to handle them differently. For an example, an excerpt from the TM API documentation (the LookupSegment operation) is shown below:
[FaultContract(typeof(InvalidSessionIdFault))]
[FaultContract(typeof(UnauthorizedAccessFault))]
[FaultContract(typeof(NoLicenseFault))]
[FaultContract(typeof(TMFault))]
[FaultContract(typeof(RequestXmlFormatFault))]
[FaultContract(typeof(UnexpectedFault)), FaultContract(typeof(GenericFault))]
[OperationContract]
string LookupSegment(string sessionId, string lookupDataXml, Guid tmGuid,LookupSegmentRequest options);
The function above can return various types of faults. Before going through each of them, it is important to emphasize, that all of these faults have hold information defined in the SOAP standard (fault code, message, and the optional detail). Besides these, extra data is be defined for some of the faults: e.g. TMFault has a member, holding the TM specific error code. Modern WS technologies (e.g. WCF) make these data available, older ones (e.g. ASMX) does not. To enable at least to differentiate between the types of the faults for these old technologies, different fault code is used for different fault types. The fault code is similar to the name of the fault contract type for most cases. E.g. if the server throws InvalidSessionIdFault, the fault code is "InvalidSessionIdFault". Some fault types hold an extra error code in the fault Detail. E.g. the TMFault fault type is used for different types of TM related faults: the TM does not exit, the TM is corrupt, etc. The ErrorCode member of the TMFault can be used to differentiate between them. However, older WS technologies do not make this extra data (ErrorCode in this case) available in an easy way: use the fault code in this case instead, which is different for each kind of TM related error, e.g. "TM+DoesNotExists" if the TM does not exist, "TM+Corrupt" if the TM is corrupt, and so on.
Now let's categorize the faults possibly returned by LookupSegment (and all other API operations).
Error categories
Unexpected server errors
For unexpected server errors UnexpectedFault is returned. All API functions return UnexpectedFault in this case in advanced error handling mode, even if the operation is not decorated by the FaultContract(typeof(UnexpectedFault))] attribute in this document! As you can see from the following declaration it has one member, holding the stack trace:
/// <summary>
/// Throw in case of unexpected errors
/// </summary>
[DataContract(Namespace = "http://kilgray.com/memoqservices/2007")]
public partial class UnexpectedFault
{
/// <summary>
/// The stack trace on the memoQ server for the operation throwing
/// the fault.
/// </summary>
[DataMember]
public string StackTrace;
}
The fault code for these errors is "Unexpected". The message of the fault is the message of the original exception thrown inside memoQ Server. Do not display it to the user (display something like "Internal server error" instead"). To find the source of the problem, check memoQ Server error log, and the message of the fault.
Specific errors supported by fault contracts
These are different kinds of expected errors the operation is annotated with as fault contracts. For the above example these are InvalidSessionIdFault , UnauthorizedAccessFault , NoLicenseFault , TMFault and RequestXmlFormatFault. Please note that even though the operation is annotated with UnexpectedFault and GenericFault, we do not consider them as belonging into this category, as all API operations are annotated by them, and they are generic in this manner.
Uncategorized expected server errors (generic faults)
These are expected errors that are not distinguished based on their fault type. For these types of errors GenericFault is returned. This fault is used when an operation can run into different expected error scenarios, but not different fault contracts have been introduced for them. A few scenarios you can receive this fault: trying to modify a project that no longer exists, trying to import into a read only TM, etc.. The fault code for these errors is currently "Generic" for all operations. However, different fault codes may be introduced in the future (e.g. "Generic.UpdateError or "Generic+ProjectNotFound"): you can assume that the all start with the word "Generic". So if you want to catch generic faults based on their fault code, the recommended way to do it is to check that the fault code starts with "Generic".
The message of the fault is a message that can be displayed to the user. The message is localized based on the language of your memoQ Server.
You should be prepared to receive this kind of fault for all API operations (even if the operation is not decorated by the FaultContract(typeof(UnexpectedFault))] attribute in this document).
Recommendation
Advanced error handling mode enables you to catch and handle errors based on their specific type. However this is not necessary in most cases. One easy to implement solution is the following:
- For unexpected server errors you can show the user an "internal server error" screen
- For generic expected faults (GenericFault), you can display the message of the fault to the user in a user friendly error screen. This is not possible however if your application is localized to multiple languages.
- For specific errors supported by fault contracts: some may require special handling, but for most of them.
- You can display the message of the fault to the user in a user friendly error screen
- Or if you application is localized to multiple languages, you can display a message defined in your system based on the type of the fault or the value of the fault code.
You probably can use the same error handling logic for most of the API calls: implement it once in a central error handler function, call this same function whichever API method throws.
Code samples
WCF enables catching the faults based on their type:
try
{
string res = tmService.LookupSegment(...);
}
catch (FaultException<InvalidSessionIdFault> e)
{
// E.g. re-login user
}
catch (FaultException<RequestXmlFormatFault> e)
{
// This is a kind of programmatic error, handle it specially
}
catch (FaultException<GenericFault> e)
{
// Uncategorized expected server errors (generic faults)
// E.g. display the message of the fault to the user
// in a user friendly error screen.
}
catch (FaultException<UnexpectedFault> e)
{
// Unexpected server errors
// E.g. show the user an "internal server error" screen
// Also log stack trace:
Log.WriteError(e.Detail.StackTrace);
}
catch (FaultException e)
{
// All other faults (UnauthorizedAccessFault, NoLicenseFault, TMFault),
// handle them the same way
}
The last catch block catches all SOAP faults. This may include connectivity, and other errors, depending on the WS technology you use.
An alternative way to handle faults is based on their fault code (with older WS technologies this is the only alternative). The possible values of the fault codes are listed in the API documentaion.
Using WCF the fault code is the Code.Name member of the caught FaultException object:
try
{
// string res = tmService.LookupSegment(...);
}
catch (FaultException e)
{
if (ex.Code.Name == "Unxecpected")
Log.WriteError("Unexpected error occoured");
}
Now, the above complete error handling mechanism is implemented based on fault codes using ASMX technology:
try
{
// string res = tmService.LookupSegment(...);
}
catch (SoapException e)
{
// All other faults (UnauthorizedAccessFault, NoLicenseFault, TMFault),
// handle them the same way
switch (e.Code.Name)
{
case "InvalidSessionIdFault":
// E.g. re-login user
break;
case "RequestXmlFormatFault":
// This is a kind of programmatic error, handle it specially
break;
case "Unexpected":
// Unexpected server errors
// E.g. show the user an "internal server error" screen
// The stack trace is in Detail in XML format, if you want it.
break;
default:
if (e.Code.Name.StartsWith("Generic"))
{
// Uncategorized expected server errors (generic faults)
// E.g. display the message of the fault to the user
// in a user friendly error screen.
}
else
{
// All other faults (UnauthorizedAccessFault, NoLicenseFault,
// TMFault, etc.),
// Handle them the same way
}
break;
}
}
The default block catches all SOAP faults. This may include connectivity, and other errors, depending on the WS technology you use.