Исключение com.sun.xml.messaging.saaj.SOAPExceptionImpl: SAAJ0511: Unable to create envelope from given source возникает в момент, когда SAAJ-фабрика пытается построить SOAP-конверт из переданного источника Source, но не может распарсить его содержимое как корректный XML-документ. Чаще всего это происходит при вызове MessageFactory.createMessage() или SOAPMessage.getSOAPBody(), когда входной поток пуст, содержит не-XML данные (например, HTML-страницу ошибки от прокси) или XML с нарушенной структурой.

Проблема типична для приложений на Java, работающих с SOAP-сервисами через JAX-WS, Spring-WS или напрямую через SAAJ API. Ошибка вводит в заблуждение: текст исключения говорит о конверте, но реальная причина почти всегда кроется в содержимом ответа сервера или в способе чтения потока. Ниже разберём диагностику и рабочие способы устранения.

Что означает код SAAJ0511

Библиотека SAAJ (SOAP with Attachments API for Java) — это низкоуровневый механизм обработки SOAP-сообщений. Когда вы передаёте в MessageFactory объект StreamSource, DOMSource или SAXSource, парсер ожидает увидеть валидный XML, корневым элементом которого является Envelope в одном из пространств имён SOAP (1.1 или 1.2).

Код SAAJ0511 выбрасывается на этапе построения конверта, если парсер не смог интерпретировать источник. Это «обёрточная» ошибка: почти всегда у неё есть вложенная причина (caused by), например XMLStreamException или SAXParseException, которая и указывает настоящее место сбоя — строку и позицию в документе.

💡

SAAJ0511 — это симптом, а не диагноз. Всегда смотрите на вложенное исключение (caused by) в стектрейсе: именно оно указывает реальную причину сбоя парсинга.

Типичные причины ошибки

Практика показывает, что источники проблемы повторяются из проекта в проект. Вот основные сценарии, которые стоит проверить в первую очередь.

  • 📄 Пустой ответ сервера — HTTP-ответ с кодом 200, но с нулевым телом: парсер получает пустой поток и не находит ни одного элемента.
  • 🌐 HTML вместо SOAP — сервер, прокси или балансировщик вернул HTML-страницу (ошибка 500, страница аутентификации, captive portal), а не SOAP-конверт.
  • 🔤 Неверная кодировка или BOM — байтовый маркер порядка или рассинхронизация Content-Type и фактической кодировки ломают чтение потока.
  • 🧱 Повреждённый XML — незакрытые теги, неэкранированные символы &, <, недопустимые управляющие символы внутри текста.
  • 🔀 Несовпадение версии SOAP — сообщение в формате SOAP 1.2 подаётся в фабрику, настроенную на SOAP 1.1 (SOAPConstants.SOAP_1_1_PROTOCOL).
  • 📦 Повторное чтение потокаInputStream уже был прочитан ранее (например, для логирования) и на момент парсинга пуст.
⚠️ Внимание: если ошибка появляется только периодически под нагрузкой, почти наверняка дело в потоках или в промежуточном сетевом оборудовании (таймауты, обрезанные ответы), а не в самом коде. Разовый сбой и стабильный сбой диагностируются по-разному.

Диагностика: как найти реальную причину

Первый шаг — увидеть, что на самом деле приходит в ваше приложение. Для этого нужно перехватить «сырой» ответ до того, как его попытается распарсить SAAJ. Самый надёжный способ — включить дамп сообщений на уровне транспорта или обернуть поток в логирующую обёртку.

В JAX-WS часто используют системное свойство для вывода сообщений (доступность зависит от реализации и версии стека, поэтому сверяйтесь с документацией вашего рантайма):

-Dcom.sun.xml.ws.transport.http.HttpAdapter.dump=true

Если вы работаете с SAAJ напрямую, прочитайте поток в строку заранее и залогируйте его. Так вы сразу увидите: пустой ли ответ, HTML ли это, или XML с дефектом. Заодно проверьте HTTP-статус и заголовок Content-Type — для SOAP 1.1 ожидается text/xml, для SOAP 1.2 — application/soap+xml.

☑️ Диагностика SAAJ0511 за 5 шагов

Выполнено: 0 / 5
📊 Где вы столкнулись с ошибкой SAAJ0511?
JAX-WS клиент
Spring-WS
Прямое использование SAAJ API
Внутри ESB или middleware

Решение: пустой или не-XML ответ сервера

Когда в логе видно, что сервер вернул пустое тело или HTML-страницу, исправлять нужно не клиентский парсер, а взаимодействие с сервером. Проверьте URL эндпоинта: опечатка в пути часто приводит к тому, что веб-сервер отдаёт страницу ошибки вместо SOAP-ответа. Аналогично ведут себя некоторые системы аутентификации — вместо отказа с SOAP Fault они возвращают HTML-форму логина.

На стороне клиента добавьте проверку перед парсингом: убедитесь, что статус ответа успешный, а тело не пустое. Это не устранит первопричину, но превратит непонятное SAAJ0511 в осмысленную ошибку с контекстом — URL, статусом и первыми байтами ответа.

💡

Сохраняйте первые 1–2 КБ «сырого» ответа в лог при любой ошибке парсинга. В большинстве инцидентов этого фрагмента достаточно, чтобы понять, что пришло вместо SOAP-конверта.

Решение: несовпадение версии SOAP

Если сообщение сформировано по спецификации SOAP 1.2, а фабрика создана для SOAP 1.1 (или наоборот), парсер может не распознать конверт: у версий различаются пространства имён корневого элемента Envelope. Проверьте, какая версия заявлена в ответе сервера, и сопоставьте её с настройкой клиента.

Для явного указания версии при создании фабрики используется перегрузка createMessage через MessageFactory.newInstance(...):

MessageFactory factory =

MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);

Как именно версия протокола задаётся в вашем стеке (JAX-WS, Spring-WS, CXF), зависит от фреймворка — сверяйтесь с его документацией. Ключевой признак этой причины: XML валиден, корневой элемент называется Envelope, но его namespace не совпадает с ожидаемым фабрикой.

Решение: проблемы с потоком и кодировкой

Объект InputStream можно прочитать только один раз. Если перед вызовом createMessage поток уже читался — например, в перехватчике для логирования — парсер получит пустой источник. Решение: прочитать поток в массив байт один раз и дальше работать с копиями через ByteArrayInputStream.

Отдельная группа проблем — кодировка. Если XML декларирует encoding="UTF-8", но фактически передан в другой кодировке, или в начале потока стоит BOM, который конкретная версия парсера обрабатывает некорректно, возможен сбой на самом первом байте. Проверить это просто: откройте сохранённый ответ в hex-редакторе и посмотрите первые байты. Нормальное начало — <?xml или сразу <soap:Envelope.

⚠️ Внимание: не «чините» кодировку слепой перекодировкой строки через new String(bytes) без указания charset — это маскирует проблему и может испортить данные. Сначала выясните, в какой кодировке реально отвечает сервер, и добивайтесь согласованности на уровне HTTP-заголовков.

Сводная таблица причин и действий

Симптом в логеВероятная причинаПервое действие
Вложенное исключение: «Premature end of file»Пустое тело ответа или уже прочитанный потокПроверить HTTP-статус и размер ответа
В ответе виден HTML-кодПрокси, страница ошибки или логина вместо SOAPПроверить URL эндпоинта и аутентификацию
«Content is not allowed in prolog»BOM, лишние байты или не-XML данные в начале потокаПосмотреть первые байты ответа в hex-виде
XML валиден, но конверт не распознаётсяНесовпадение версий SOAP 1.1 / 1.2Сверить namespace Envelope и настройку фабрики
Ошибка на конкретной строке XMLНеэкранированные символы, битая структураПровалидировать ответ внешним XML-валидатором
💡

Порядок диагностики всегда один: сначала получить сырой ответ, потом проверить его валидность, и только затем трогать код парсинга. 9 из 10 случаев SAAJ0511 решаются на первом шаге.

Профилактика: как не столкнуться с ошибкой снова

Чтобы подобные сбои не превращались в многочасовую отладку, встройте в интеграционный слой несколько простых практик. Во-первых, логируйте сырые запросы и ответы на уровне транспорта — с ограничением размера и маскировкой чувствительных данных. Во-вторых, валидируйте HTTP-уровень (статус, Content-Type, непустое тело) до передачи потока в SAAJ.

В-третьих, зафиксируйте версию SOAP-протокола в конфигурации клиента явно, а не полагайтесь на значения по умолчанию, которые могут отличаться между реализациями. И наконец, при интеграции с новым сервисом сначала проверьте его ответ независимым инструментом — так вы отделите проблемы сервиса от проблем вашего кода.

Почему текст ошибки вводит в заблуждение

Формулировка «unable to create envelope» описывает внутренний этап работы SAAJ — построение объекта Envelope из дерева XML. Парсер не различает «плохой XML» и «вообще не XML»: для него и пустой поток, и HTML-страница, и битый документ выглядят одинаково — как невозможность построить конверт. Поэтому диагностическая ценность самого сообщения SAAJ0511 невысока, и вся полезная информация всегда находится во вложенном исключении и в содержимом ответа.

Частые вопросы

Может ли SAAJ0511 возникать из-за сетевых проблем?

Да, косвенно. Таймауты, обрывы соединения или сжатие ответа, которое клиент не смог раскодировать, приводят к тому, что в парсер попадает пустой или обрезанный поток. Проверяйте HTTP-статус, заголовки Content-Encoding и полноту тела ответа.

Как посмотреть вложенную причину исключения?

В стектрейсе ищите строки, начинающиеся с Caused by: — их может быть несколько уровней. Самое нижнее исключение обычно и есть первопричина: SAXParseException с номером строки, XMLStreamException или ошибка ввода-вывода.

Ошибка появляется только в продакшене, локально всё работает. Что проверить?

Типичные различия сред: прокси-серверы и балансировщики, другой URL эндпоинта, требования аутентификации, отличия в TLS и различия версий библиотек SAAJ в контейнере. Начните с захвата сырого ответа именно в проблемной среде.

Поможет ли обновление библиотеки SAAJ?

Только если причина — известный дефект конкретной реализации парсера (например, некорректная обработка BOM или кодировок). В большинстве случаев ошибка вызвана данными, а не библиотекой, поэтому обновление без диагностики ответа результата не даст.

Можно ли подавить ошибку и вернуть пустой SOAP-ответ?

Технически можно перехватить исключение и подставить заглушку, но это скроет реальную проблему интеграции и усложнит поддержку. Корректный подход — выбросить собственное информативное исключение с контекстом: URL, статусом и фрагментом ответа.