Исключение 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 шагов
Решение: пустой или не-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, статусом и фрагментом ответа.