Solutions BlogCloud Integration6 min readOctober 2026

Fixing “Content is not allowed in prolog” in SAP Cloud Integration

Why XML steps fail on payloads that look valid, and a reusable Groovy pattern that cleans the payload and routes it by type.

By the Klynexa integration team

Applies to
SAP Integration Suite, Cloud Integration
Steps involved
Groovy Script, Router, XML steps
Typical senders
SFTP files, HTTP and REST callers

The problem

An integration flow receives a payload that looks like perfectly valid XML, yet the first XML step (an XSLT mapping, an XML validator or a message mapping) fails with:

org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1;
Content is not allowed in prolog.

The error says the parser found something before the opening < of the document. Opening the payload in a trace often shows nothing wrong, which is what makes this one frustrating.

Root causes we see most often

  • A byte order mark (BOM) at the start of a file, typically from files created on Windows and picked up over SFTP. It is invisible in most viewers.
  • Leading whitespace or line breaks added by a sender or by an earlier step in the flow.
  • The payload is not XML at all. A sender switched to JSON, or an error response (HTML or JSON) came back from a receiver and continued down the flow.
  • An empty body reaching the XML step after a request-reply that returned no content.

The solution

Add a Groovy script step directly before the first XML step. It removes a BOM and surrounding whitespace, then records what kind of payload arrived so a router can send it down the correct path.

import com.sap.gateway.ip.core.customdev.util.Message

def Message processData(Message message) {
    def body = message.getBody(String) ?: ""

    // Remove a UTF-8 byte order mark if present
    if (body.startsWith("\uFEFF")) {
        body = body.substring(1)
    }
    body = body.trim()

    // Record the payload type for routing
    def type = "unknown"
    if (!body) {
        type = "empty"
    } else if (body.startsWith("<")) {
        type = "xml"
    } else if (body.startsWith("{") || body.startsWith("[")) {
        type = "json"
    }

    message.setProperty("payloadType", type)
    message.setBody(body)
    return message
}

Then add a Router with conditions on ${property.payloadType}:

  • xml continues to your mapping as before.
  • json goes through a JSON to XML converter first, if the sender legitimately sends JSON.
  • empty and unknown go to an error branch that raises an alert with the first part of the payload logged, so support can see exactly what arrived.

Result

The flow no longer fails on invisible characters, genuinely bad payloads are caught with a clear reason instead of a parser error, and support teams can see what was received without enabling trace.

Prevention

Agree the encoding (UTF-8 without BOM) and the format with every file-based sender, and keep this cleaning step in your standard iFlow template for all file and HTTP inbound flows.