If this exact error string is showing up in your logs, the upstream SOAP server received your request but failed while deserializing the XML body into its server-side object model.
System.Web.Services.Protocols.SoapException: Server was unable to read request.
---> System.InvalidOperationException: There is an error in XML document
In plain English: the XML envelope reached the server, but at least one element, type, namespace, or SOAP version detail did not match what the service expected.
What usually causes it
The most common causes are:
- sending
"value"or another string into anxsd:intfield - missing required XML elements
- wrong namespace prefixes on nested fields
- sending a SOAP 1.2 envelope to a SOAP 1.1 endpoint, or the reverse
- using the wrong operation wrapper element
This is why calculator-style services often fail on a request like:
{
"intA": "value",
"intB": "value"
}
when the service really expects integer-compatible values.
Fastest way to confirm the root cause
Capture the raw request body and compare it to the WSDL schema or a known-good request from SoapUI.
curl -v https://example.com/service.asmx \
-H 'Content-Type: text/xml; charset=utf-8' \
-H 'SOAPAction: "http://tempuri.org/Add"' \
--data @request.xml
Check these four things first:
- The outer SOAP envelope namespace matches the endpoint version.
- The operation element name exactly matches the service contract.
- Required fields are present.
- Primitive values are actually parseable as the declared types.
Common broken vs correct payload
Broken:
<Add xmlns="http://tempuri.org/">
<intA>value</intA>
<intB>value</intB>
</Add>
Correct:
<Add xmlns="http://tempuri.org/">
<intA>2</intA>
<intB>3</intB>
</Add>
If the service expects xsd:dateTime, the same rule applies. A value like 2026-03-22 may fail where 2026-03-22T00:00:00 succeeds.
Namespace mistakes are the silent killer
Even if the XML looks visually correct, nested elements in the wrong namespace can trigger the same deserialization failure.
<tns:GetUser xmlns:tns="http://example.com/service">
<userId>42</userId>
</tns:GetUser>
Some servers accept this. Many older ASP.NET SOAP services do not. They want the child element in the same namespace:
<tns:GetUser xmlns:tns="http://example.com/service">
<tns:userId>42</tns:userId>
</tns:GetUser>
SOAP 1.1 vs SOAP 1.2 mismatch
If the endpoint is .asmx, SOAP 1.1 is still common.
- SOAP 1.1 usually uses
text/xmland a separateSOAPActionheader - SOAP 1.2 usually uses
application/soap+xml
If you send the wrong envelope style, some services surface a version mismatch cleanly. Others collapse into the same generic XML document error.
Minimal debugging workflow
Use this order:
- Fetch and inspect the WSDL
- Build a minimal request with only required fields
- Replace all sample placeholder values with real typed values
- Compare the raw envelope with SoapUI output
- Confirm SOAP version and
SOAPAction
curl -s "https://example.com/service.asmx?WSDL" -o service.wsdl
xmllint --noout service.wsdl
Practical fix if you are tired of hand-maintaining envelopes
If this failure keeps recurring across environments, the real problem is not just the one bad XML document. The real problem is that every caller has to keep rebuilding a fragile SOAP envelope exactly right.
SOAPless turns the WSDL into a REST JSON endpoint, validates the request shape before the upstream call, and keeps the XML construction in one place instead of scattering it across scripts and application code.