Stream Closed Before Completed
Четыре причины и метод диагностики по хопам
Потоковый ответ обрывается на полпути с текстом ошибки "stream closed before completed" — есть четыре распространённые причины, и это совершенно разные проблемы; разобраться можно только проверив каждый хоп по отдельности.
Четыре ключевых факта
Четыре распространённые причины
Фрейм ограничения скорости от апстрима, несоответствие таймаутов в цепочке, отключение клиента или слой перезаписи, теряющий финальное событие — выглядят похоже, но причины совершенно разные.
Суть метода диагностики
Путь от клиента до сервиса модели обычно проходит через несколько хопов (клиент → шлюз → балансировщик → апстрим); чтобы найти, где именно произошёл обрыв, нужно проверить каждый хоп по отдельности.
Дошло ли финальное событие
Многие потоковые протоколы отмечают нормальное завершение специальным событием (например [DONE]); если поток оборвался, а это событие так и не появилось — значит поток прервали, а не завершили нормально.
Самая часто упускаемая причина
Клиент, шлюз и балансировщик нагрузки обычно задают свои собственные таймауты; если таймаут хотя бы на одном хопе короче реального времени генерации, именно этот хоп принудительно оборвёт поток.
Что представляет собой каждая из четырёх причин
Первая, фрейм ограничения скорости от апстрима: сервис модели упирается в рейт-лимит прямо во время генерации и вставляет сигнал прерывания, досрочно завершающий генерацию — клиент просто видит, что поток оборвался, не дойдя до конца. Вторая, несоответствие таймаутов: запрос проходит через несколько хопов — клиент, прокси, шлюз, балансировщик — и если настроенный таймаут хотя бы на одном из них короче реального времени генерации модели, этот хоп принудительно разорвёт соединение, даже если модель продолжает генерировать нормально. Третья, отключение клиента (client gone): пользователь закрыл страницу или отменил запрос на полпути, либо контейнер/процесс клиента был утилизирован — такой обрыв обычно оставляет в серверных логах запись вроде «peer closed connection» и указывает в противоположную от первых двух причин сторону. Четвёртая, слой перезаписи теряет финальное событие: некоторые обратные прокси, CDN или перезаписывающее middleware заново обрабатывают тело потокового ответа, и при ошибке в реализации могут корректно передать сам контент, но потерять финальное событие, отмечающее «генерация завершена нормально» (например [DONE] в SSE) — тогда клиент решает, что соединение оборвалось аварийно, хотя модель на самом деле благополучно завершила генерацию.
Почему стоит запомнить этот метод диагностики
Из четырёх причин первые две (фрейм ограничения скорости, несоответствие таймаутов) чаще всего принимают за «у нас нестабильная сеть». Третью (отключение клиента) сервер часто ошибочно списывает на «модель нестабильна». Четвёртая (слой перезаписи теряет финальное событие) — самая незаметная, потому что содержимое, которое получает клиент, выглядит полным — просто без одного характерного сигнала завершения — и её легко списать на «случайный баг», а не системную проблему.
Хронология
Потоковые ответы (SSE и похожие протоколы) и механизм финального события — давно устоявшийся стандартный дизайн; все четыре причины обрыва существуют столько же, сколько распространён стриминг.
Агентные workflow увеличивают число долгих потоковых генераций, из-за чего плохо настроенные таймауты где-либо в цепочке проявляются чаще.
Самая незаметная причина — слой перезаписи, теряющий финальное событие — встречается всё чаще по мере усложнения middleware обратных прокси и CDN.
Подтверждено vs частое заблуждение
Подтверждено
Сообщение «stream closed before completed» стабильно встречается в публичных обсуждениях разработчиков; все четыре причины (фрейм ограничения скорости, несоответствие таймаутов, отключение клиента, потеря события слоем перезаписи) задокументированы по отдельности и требуют раздельной диагностики.
Частое заблуждение
Первая реакция многих на обрыв потока — «модель нестабильна» или «проблема на стороне вендора». Но на практике несоответствие таймаутов и баги слоя перезаписи — оба являются проблемами настройки или реализации на стороне клиента/middleware — составляют значительную долю случаев.
Как отличить четыре причины
Обрыв со стороны сервера (рейт-лимит / таймаут)
Серверные логи покажут, что генерация была ограничена по скорости или обрыв вызван таймаутом на каком-то хопе; характерный признак — тот же запрос часто успешно завершается в другое время суток или при меньшей параллельности.
Проблема на стороне клиента (отключение / потеря события слоем перезаписи)
Серверные логи показывают, что генерация на самом деле завершилась нормально — проблема в том, что клиент отключился слишком рано, либо промежуточный слой перезаписи не передал финальное событие. Здесь нужно проверять настройки клиента/прокси, а не сервис модели.
Метод диагностики по хопам
Шаг 1: посмотрите серверные логи — завершилась ли генерация полностью, был ли рейт-лимит. Если серверная запись показывает нормальное завершение, проблема, скорее всего, на каком-то хопе между клиентом и сервером. Шаг 2: проверьте настройки таймаутов на каждом хопе по отдельности — таймаут клиента, CDN/шлюза, балансировщика нагрузки — и найдите, какой из них короче реального времени генерации модели. Шаг 3: если подозреваете потерю события слоем перезаписи, напрямую сравните «исходный поток, отправленный сервером» с «потоком, полученным клиентом», и проверьте, не потерялось ли финальное событие где-то посередине. Шаг 4: если это намеренное отключение клиента (например, пользователь вручную отменил запрос), это вообще не сбой — нужно просто корректно обрабатывать такую отмену в логике приложения, а не расследовать как системный сбой.
Что делать в QCode
Периферийные узлы QCode выравнивают таймауты для потоковых ответов, снижая число случаев, когда несоответствующий таймаут на каком-то хопе ошибочно обрывает долгую генерацию; если у вашего собственного прокси или перезаписывающего middleware похожая проблема, можно временно перейти на эндпоинт QCode для диагностики.
Частые вопросы
Как понять, что обрыв потока — проблема на моей стороне?
Сначала посмотрите серверные логи, если они доступны: если там указано, что генерация завершилась нормально, проблема, скорее всего, на каком-то хопе между клиентом и сервером (настройки таймаута, слой перезаписи и т.д.) — это то, что можно диагностировать и исправить самостоятельно.
Почему один и тот же запрос иногда работает нормально, а иногда обрывается?
Самая частая причина — несоответствие таймаутов: время генерации естественным образом варьируется, и когда оно превышает самый короткий таймаут где-либо в цепочке, запрос обрывается, а более быстрые запросы завершаются нормально.
Считается ли отключение клиента сбоем?
Строго говоря, нет — это нормальное поведение пользователя (отмена запроса, закрытие страницы) или изменение окружения клиента (утилизация процесса); нужно просто корректно распознавать и обрабатывать этот случай в логике приложения, а не расследовать как системный сбой.
Как быстрее всего диагностировать потерю финального события слоем перезаписи?
Захватите трафик или добавьте логирование, напрямую сравнив то, что сервер реально передал в потоке, с тем, что в итоге получил клиент — если сервер отправил финальное событие, а клиент его не получил, проблема в промежуточном слое перезаписи/проксирования.
Что проверить в первую очередь при этой ошибке?
Сначала проверьте серверные логи на предмет рейт-лимита или ошибок; если там ничего нет, сверьте настройки таймаутов клиента, шлюза и балансировщика нагрузки — эти два шага исключают три из четырёх причин.
Это то же самое, что 429/529?
Нет. 429/529 означают, что запрос отклонён ещё до начала генерации; stream closed before completed означает, что генерация уже началась, поток уже открыт, но был прерван до завершения — это совершенно разные стадии.
Источники
Четыре причины собраны на основе публичных обсуждений разработчиков, стандартного поведения потоковых протоколов вроде SSE и общей практики диагностики таймаутов по хопам; эта страница не содержит утверждений о приватной реализации какого-либо конкретного поставщика. Составлено 27.08.2026.
Обрывы потока не должны тормозить работу
Периферийные узлы QCode выравнивают таймауты потоковых ответов, снижая число ошибочных обрывов долгих генераций.
Читайте также
Context Length Exceeded: полное руководство по диагностике
Ещё одна часто неверно диагностируемая ошибка — полезно для сравнения.
Полный гид по проверке использования API-ключа
Как системно проверять историю своих вызовов и аномалии.
Claude 529 vs 429 vs Weekly Limit
Ещё один набор легко путаемых ошибок, разобранный по полочкам.
Эта страница — общее техническое объяснение для разных поставщиков и не содержит утверждений о приватной реализации какого-либо конкретного из них. Фактическое поведение зависит от используемой модели, клиента и настроек цепочки прокси.