openai/openai-python · error · RuntimeError

A single event handler cannot be shared between multiple str

Error message

A single event handler cannot be shared between multiple streams; You will need to construct a new event handler instance

What it means

This RuntimeError is thrown by AssistantEventHandler._init when the same handler instance is passed to a second streaming call. The handler stores per-stream state (the Stream reference, accumulated snapshots, iterators created in __init__), so entering a new stream with an already-bound handler would mix state from two runs. The library therefore forbids reuse after the handler has been entered via __enter__/__aenter__.

Source

Thrown at src/openai/lib/streaming/_assistants.py:63

    def __init__(self) -> None:
        self._current_event: AssistantStreamEvent | None = None
        self._current_message_content_index: int | None = None
        self._current_message_content: MessageContent | None = None
        self._current_tool_call_index: int | None = None
        self._current_tool_call: ToolCall | None = None
        self.__current_run_step_id: str | None = None
        self.__current_run: Run | None = None
        self.__run_step_snapshots: dict[str, RunStep] = {}
        self.__message_snapshots: dict[str, Message] = {}
        self.__current_message_snapshot: Message | None = None

        self.text_deltas = self.__text_deltas__()
        self._iterator = self.__stream__()
        self.__stream: Stream[AssistantStreamEvent] | None = None

    def _init(self, stream: Stream[AssistantStreamEvent]) -> None:
        if self.__stream:
            raise RuntimeError(
                "A single event handler cannot be shared between multiple streams; You will need to construct a new event handler instance"
            )

        self.__stream = stream

    def __next__(self) -> AssistantStreamEvent:
        return self._iterator.__next__()

    def __iter__(self) -> Iterator[AssistantStreamEvent]:
        for item in self._iterator:
            yield item

    @property
    def current_event(self) -> AssistantStreamEvent | None:
        return self._current_event

    @property
    def current_run(self) -> Run | None:

View on GitHub (pinned to 9917c6e28e)

Solutions

  1. Construct a fresh AssistantEventHandler (or subclass) instance for every stream call
  2. If you loop over prompts, move handler construction inside the loop
  3. If you need shared logic, put it in the handler class (override on_event etc.) rather than sharing one instance
  4. Check that you are not accidentally passing the same handler to both a retry and the original call

Example fix

# before
handler = MyHandler()
for question in questions:
    with client.beta.threads.runs.stream(thread_id=..., assistant_id=..., event_handler=handler) as stream:
        stream.until_done()

# after
for question in questions:
    handler = MyHandler()  # new instance per stream
    with client.beta.threads.runs.stream(thread_id=..., assistant_id=..., event_handler=handler) as stream:
        stream.until_done()
Defensive patterns

Strategy: validation

Validate before calling

def assert_fresh_handler(handler) -> None:
    if getattr(handler, "_AssistantEventHandler__stream", None):
        raise RuntimeError("handler already bound to a previous stream; construct a new one")

Type guard

def is_unbound_handler(h: AssistantEventHandler) -> bool:
    return getattr(h, "_AssistantEventHandler__stream", None) is None

Try / catch

try:
    with client.beta.threads.runs.stream(..., event_handler=handler) as s:
        s.until_done()
except RuntimeError as e:
    if "cannot be shared" in str(e):
        handler = MyHandler()  # fresh instance, then retry once

Prevention

When it happens

Trigger: Calling client.beta.threads.runs.stream(...) (or create_and_stream) twice with the same AssistantEventHandler instance, e.g. in a loop: for q in questions: with client.beta.threads.runs.stream(thread_id=..., assistant_id=..., event_handler=handler). Any second stream entered with a handler whose _init already ran will raise.

Common situations: Retrying or looping over multiple user questions with one handler; sharing a module-level handler; partial-migration from the old Assistants streaming API where reuse appeared to work; forgetting that entering the handler in a with-block binds it permanently.

Related errors


AI-assisted analysis of openai/openai-python@9917c6e28e (2026-08-28). Data as JSON: /api/errors/cf87d768af42c7e8. Report an issue: GitHub.