{"record":{"id":"9307e5b2380ea6c6","repo":"BerriAI/litellm","slug":"stream-completed-response-is-invalid","errorCode":null,"errorMessage":"Stream completed response is invalid","messagePattern":"Stream completed response is invalid","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/completion_extras/litellm_responses_transformation/handler.py","lineNumber":84,"sourceCode":"                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)\n        if not isinstance(response, ResponsesAPIResponse):\n            raise ValueError(\"Stream completed response is invalid\")\n        return response\n\n    def validate_input_kwargs(self, kwargs: dict) -> ResponsesToCompletionBridgeHandlerInputKwargs:","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/completion_extras/litellm_responses_transformation/handler.py#L66-L102","documentation":"Raised by the sync stream-collection path of the chat-completions-to-Responses-API bridge. After the stream is fully drained, the handler reads stream_iter.completed_response.response, coerces it via _coerce_response_object, and requires the result to be a ResponsesAPIResponse. If coercion succeeds but the object still fails the isinstance check, the completed payload is malformed. In practice this is a defensive guard: it fires only on litellm internal bugs, version skew, or exotic custom stream wrappers.","triggerScenarios":"litellm.completion(..., stream=True) on a model routed through the responses bridge where the CustomStreamWrapper finishes without a well-formed completed_response.response object (e.g. a custom_llm_provider stream wrapper, a cached_response stream, or a partially-accumulated completed object after early termination).","commonSituations":"Custom providers or proxies that return their own stream iterators without completed_response; mocked/fake streams in tests; mixing litellm versions where the wrapper no longer populates completed_response; debugging sessions that intercept the stream.","solutions":["Retry the same call with stream=False so the non-streaming bridge path is used, to confirm the model itself works","Upgrade litellm to the latest patch release (bridge/stream-wrapper contract fixes land frequently)","Inspect stream_iter.completed_response and its .response in a debugger right after iteration to see what object was accumulated","If you pass a custom stream wrapper, ensure it sets completed_response.response to a ResponsesAPIResponse-shaped object before exhaustion","File a litellm issue with the model, provider, and stream payload if it reproduces on the latest version"],"exampleFix":"// before\nresp = bridge_handler.completion(**kwargs, stream=True)  # ValueError: Stream completed response is invalid\n\n// after\nresp = litellm.completion(model=model, messages=messages, stream=False)  # bypass stream collection while debugging","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"from litellm.types.llms.openai import ResponsesAPIResponse\n\ndef has_valid_completed_response(stream_iter) -> bool:\n    completed = getattr(stream_iter, \"completed_response\", None)\n    resp = getattr(completed, \"response\", None) if completed else None\n    return isinstance(resp, ResponsesAPIResponse) or isinstance(resp, dict)","tryCatchPattern":"try:\n    resp = litellm.completion(model=m, messages=msgs, stream=True)\nexcept ValueError as e:\n    if \"Stream completed response is invalid\" in str(e):\n        resp = litellm.completion(model=m, messages=msgs, stream=False)  # non-stream fallback\n    else:\n        raise","preventionTips":["Keep litellm upgraded so the stream wrapper and bridge share one contract","If you inject custom stream wrappers, always set completed_response.response before exhaustion","Smoke-test streaming paths after every litellm upgrade"],"tags":["streaming","responses-api","type-validation","internal-api"],"backgroundTag":"invalid-stream-response-shape","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}