SOAP faults and error handling

A SOAP fault inside a 500 response is still a structured error message, and mapping it to the right client exception takes a deliberate decision.

The fault structure

<!-- SOAP 1.1 -->
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <soap:Fault>
      <faultcode>soap:Client</faultcode>
      <faultstring>SKU A-1 is not available</faultstring>
      <faultactor>urn:example:orders</faultactor>
      <detail>
        <OrderFault xmlns="urn:example:orders">
          <code>SKU_UNAVAILABLE</code>
          <sku>A-1</sku>
        </OrderFault>
      </detail>
    </soap:Fault>
  </soap:Body>
</soap:Envelope>
<!-- SOAP 1.2 -->
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
  <env:Body>
    <env:Fault>
      <env:Code>
        <env:Value>env:Sender</env:Value>
        <env:Subcode><env:Value>rpc:BadArguments</env:Value></env:Subcode>
      </env:Code>
      <env:Reason>
        <env:Text xml:lang="en">SKU A-1 is not available</env:Text>
      </env:Reason>
      <env:Detail>
        <OrderFault xmlns="urn:example:orders"><code>SKU_UNAVAILABLE</code></OrderFault>
      </env:Detail>
    </env:Fault>
  </env:Body>
</env:Envelope>
1.1 faultcode1.2 Code/ValueMeaningClient action
VersionMismatchenv:VersionMismatchWrong envelope namespaceFix the version, do not retry
MustUnderstandenv:MustUnderstandA required header was not processedFix the header, do not retry
Clientenv:SenderThe request is at faultDo not retry as-is
Serverenv:ReceiverThe service failedRetry with backoff, then alert
DataEncodingUnknownenv:DataEncodingUnknownUnsupported encoding declarationFix the request

Handling faults in client code

import javax.xml.ws.WebServiceException;
import javax.xml.ws.soap.SOAPFaultException;
import javax.xml.soap.SOAPFault;
import javax.xml.soap.Detail;

try {
  port.placeOrder(order);
} catch (SOAPFaultException e) {
  SOAPFault f = e.getFault();
  String code = f.getFaultCode();
  Detail detail = f.getDetail();
  // Distinguish a retryable server fault from a permanent request error
  if ("Server".equals(code) || "env:Receiver".equals(code)) {
    throw new RetryableException(f.getFaultString(), e);
  }
  throw new InvalidRequestException(f.getFaultString(), detail, e);
} catch (WebServiceException e) {
  // Transport level: DNS, TLS, timeout, connection reset
  throw new RetryableException("transport failure", e);
}
  • A fault is a first-class application message, not merely an HTTP error. Preserve faultcode, faultstring and the detail into your own error type.
  • Distinguish three layers: transport failure, SOAP protocol fault, and application error carried in the detail.
  • A timeout is not a confirmed failure. A retried write after a timeout can duplicate the operation — the fix is an idempotency key, not blind retries.
  • Application errors that are expected in normal operation are better modelled as a normal response with a status field than as a fault, which tooling treats as exceptional.
  • faultactor only matters when an intermediary, such as a gateway, is reporting the fault.
  • Never surface a raw stack trace in detail — it leaks internals and is often copied into logs by clients.
⚠️
Some frameworks throw a generic exception when the response body is a fault but the HTTP status is 200. If your error handling depends on the status code alone, those failures arrive as a parse error on a success path — always inspect the body of an unexpectedly shaped response.

FAQ

Should my service return a fault for validation errors?
For a SOAP contract, yes — a fault with a structured detail is the contract-conformant way to report a rejected request. Just make sure the detail is a documented schema type rather than a free-text string.
Why does the client see a NullPointerException instead of my fault?
Because the generated proxy could not map the detail element to the declared fault bean. Make sure the fault is declared in the WSDL for that operation and the detail matches the declared type exactly.

Calling a SOAP service, and why REST usually wins Testing and debugging SOAP services

Last refreshed 2026-09-18.