{"record":{"id":"80f1b88460ca1adb","repo":"BerriAI/litellm","slug":"stream-ended-without-a-completed-response","errorCode":null,"errorMessage":"Stream ended without a completed response","messagePattern":"Stream ended without a completed response","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/completion_extras/litellm_responses_transformation/handler.py","lineNumber":79,"sourceCode":"            raise ValueError(\"Unexpected responses stream payload\")\n\n        if hidden_params:\n            existing: Final = getattr(response, \"_hidden_params\", None)\n            if not isinstance(existing, dict) or not existing:\n                setattr(response, \"_hidden_params\", dict(hidden_params))\n            else:\n                for key, value in hidden_params.items():\n                    existing.setdefault(key, value)\n        return response\n\n    def _collect_response_from_stream(self, stream_iter: Any) -> \"ResponsesAPIResponse\":\n        for _ in stream_iter:\n            pass\n\n        completed: Final = getattr(stream_iter, \"completed_response\", None)\n        response_obj: Final = getattr(completed, \"response\", None) if completed else None\n        if response_obj is None:\n            raise ValueError(\"Stream ended without a completed response\")\n\n        hidden_params: Final = getattr(stream_iter, \"_hidden_params\", None)\n        response: Final = self._coerce_response_object(response_obj, hidden_params)\n        if not isinstance(response, ResponsesAPIResponse):\n            raise ValueError(\"Stream completed response is invalid\")\n        return response\n\n    async def _collect_response_from_stream_async(self, stream_iter: Any) -> \"ResponsesAPIResponse\":\n        async for _ in stream_iter:\n            pass\n\n        completed: Final = getattr(stream_iter, \"completed_response\", None)\n        response_obj: Final = getattr(completed, \"response\", None) if completed else None\n        if response_obj is None:\n            raise ValueError(\"Stream ended without a completed response\")\n\n        hidden_params: Final = getattr(stream_iter, \"_hidden_params\", None)\n        response: Final = self._coerce_response_object(response_obj, hidden_params)","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/completion_extras/litellm_responses_transformation/handler.py#L61-L97","documentation":"The sync path of the responses bridge drains the stream iterator, then reads stream_iter.completed_response.response to rebuild the full response. If the stream ends without that attribute chain being populated (no completed_response object, or its .response is None), the bridge cannot produce a result and raises this ValueError. It means the stream terminated abnormally — often because the underlying provider stream raised or was closed early and the completion assembly never ran.","triggerScenarios":"Calling the responses-to-completion bridge synchronously on a stream that errors partway, is closed by the server, or comes from a transformation that never sets completed_response; also with mock/custom stream iterators lacking the attribute.","commonSituations":"Provider disconnects mid-stream; timeouts consuming the iterator; custom wrappers around the stream that drop the completed_response attribute; version mismatches in custom transformation code.","solutions":["Check upstream logs/exceptions for the real cause — the stream usually failed before completion assembly","Update litellm to pick up stream-completion fixes: pip install -U litellm","If wrapping the stream iterator yourself, preserve completed_response (delegate getattr to the wrapped iterator)"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"def stream_has_completed_response(stream_iter) -> bool:\n    return getattr(getattr(stream_iter, 'completed_response', None), 'response', None) is not None\n\n# only meaningful after draining; guard collection:\n# drained = list(stream_iter); assert stream_has_completed_response(stream_iter)","typeGuard":null,"tryCatchPattern":"try:\n    response = handler._collect_response_from_stream(stream)\nexcept ValueError as e:\n    if 'Stream ended without a completed response' in str(e):\n        # stream died mid-way; safe to retry the whole request\n        response = retry_request()\n    else:\n        raise","preventionTips":["Retry the full request when a stream ends incomplete — partial state cannot be salvaged","Set sane read timeouts so dead streams fail fast","If wrapping stream iterators, forward completed_response via a property"],"tags":["responses-api","streaming","bridge","incomplete-response","sync"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}