/// <summary>
        /// Gets a custom exception that wraps the SOAP exception thrown
        /// by the server.
        /// </summary>
        /// <param name="ex">SOAPException that was thrown by the server.</param>
        /// <returns>A custom exception object that wraps the SOAP exception.
        /// </returns>
        protected override Exception GetCustomException(SoapException exception)
        {
            string defaultNs = GetDefaultNamespace();

            if (!string.IsNullOrEmpty(defaultNs) && exception.Detail != null)
            {
                // Extract the ApiExceptionFault node.
                XmlElement faultNode = GetFaultNode(exception, defaultNs, "ApiExceptionFault");

                if (faultNode != null)
                {
                    try {
                        AdWordsApiException awapiException = new AdWordsApiException(
                            SerializationUtilities.DeserializeFromXmlTextCustomRootNs(
                                faultNode.OuterXml, Assembly.GetExecutingAssembly().GetType(
                                    this.GetType().Namespace + ".ApiException"), defaultNs, "ApiExceptionFault"),
                            AdWordsErrorMessages.AnApiExceptionOccurred, exception);
                        if (AdWordsErrorHandler.IsOAuthTokenExpiredError(awapiException))
                        {
                            return(new AdWordsCredentialsExpiredException(
                                       (string)ContextStore.GetValue("OAuthHeader")));
                        }
                        else
                        {
                            return(awapiException);
                        }
                    } catch (Exception) {
                        // deserialization failed, but we can safely ignore it.
                    }
                }
            }
            return(new AdWordsApiException(null, AdWordsErrorMessages.AnApiExceptionOccurred, exception));
        }