# Buggy code
def _handle_event(self, event: Event) -> CommandGenerator[None]:
    if isinstance(event, Start):
        self.h2_conn.initiate_connection()
        yield SendData(self.conn, self.h2_conn.data_to_send())

    elif isinstance(event, HttpEvent):
        if isinstance(event, self.SendData):
            assert isinstance(event, (RequestData, ResponseData))
            self.h2_conn.send_data(event.stream_id, event.data)
        elif isinstance(event, self.SendEndOfMessage):
            stream = self.h2_conn.streams.get(event.stream_id)
            if stream.state_machine.state not in (
                h2.stream.StreamState.HALF_CLOSED_LOCAL,
                h2.stream.StreamState.CLOSED,
            ):
                self.h2_conn.end_stream(event.stream_id)
            if self.is_closed(event.stream_id):
                self.streams.pop(event.stream_id, None)
        elif isinstance(event, self.SendProtocolError):
            assert isinstance(event, (RequestProtocolError, ResponseProtocolError))
            stream = self.h2_conn.streams.get(event.stream_id)
            if stream.state_machine.state is not h2.stream.StreamState.CLOSED:
                code = {
                    status_codes.CLIENT_CLOSED_REQUEST: h2.errors.ErrorCodes.CANCEL,
                }.get(event.code, h2.errors.ErrorCodes.INTERNAL_ERROR)
                self.h2_conn.reset_stream(event.stream_id, code)
            if self.is_closed(event.stream_id):
                self.streams.pop(event.stream_id, None)
        else:
            raise AssertionError(f"Unexpected event: {event}")
        data_to_send = self.h2_conn.data_to_send()
        if data_to_send:
            yield SendData(self.conn, data_to_send)

    elif isinstance(event, DataReceived):
        try:
            try:
                events = self.h2_conn.receive_data(event.data)
            except ValueError as e:  # pragma: no cover
                # this should never raise a ValueError, but we triggered one while fuzzing:
                # https://github.com/python-hyper/hyper-h2/issues/1231
                # this stays here as defense-in-depth.
                raise h2.exceptions.ProtocolError(
                    f"uncaught hyper-h2 error: {e}"
                ) from e
        except h2.exceptions.ProtocolError as e:
            events = [e]

        for h2_event in events:
            if self.debug:
                yield Log(f"{self.debug}[h2] {h2_event}", "debug")
            if (yield from self.handle_h2_event(h2_event)):
                if self.debug:
                    yield Log(f"{self.debug}[h2] done", "debug")
                return

        data_to_send = self.h2_conn.data_to_send()
        if data_to_send:
            yield SendData(self.conn, data_to_send)

    elif isinstance(event, ConnectionClosed):
        yield from self.close_connection("peer closed connection")
    else:
        raise AssertionError(f"Unexpected event: {event!r}")


# Traceback (AttributeError)
127.0.0.1:49187: mitmproxy has crashed!
Traceback (most recent call last):
File "/mnt/c/Users/user/git/mitmproxy/mitmproxy/proxy/server.py", line 276, in server_event
for command in layer_commands:
File "/mnt/c/Users/user/git/mitmproxy/mitmproxy/proxy/layer.py", line 114, in handle_event
yield from self.__process(command_generator)
File "/mnt/c/Users/user/git/mitmproxy/mitmproxy/proxy/layer.py", line 142, in __process
command = next(command_generator)
File "/mnt/c/Users/user/git/mitmproxy/mitmproxy/proxy/layer.py", line 186, in handle_event
yield from self._handle(event)
File "/mnt/c/Users/user/git/mitmproxy/mitmproxy/proxy/layer.py", line 114, in handle_event
yield from self.__process(command_generator)
File "/mnt/c/Users/user/git/mitmproxy/mitmproxy/proxy/layer.py", line 142, in __process
command = next(command_generator)
File "/mnt/c/Users/user/git/mitmproxy/mitmproxy/proxy/layers/http/__init__.py", line 570, in _handle_event
yield from self.event_to_child(stream, event)
File "/mnt/c/Users/user/git/mitmproxy/mitmproxy/proxy/layers/http/__init__.py", line 586, in event_to_child
for command in child.handle_event(event):
File "/mnt/c/Users/user/git/mitmproxy/mitmproxy/proxy/layer.py", line 114, in handle_event
yield from self.__process(command_generator)
File "/mnt/c/Users/user/git/mitmproxy/mitmproxy/proxy/layer.py", line 142, in __process
command = next(command_generator)
File "/mnt/c/Users/user/git/mitmproxy/mitmproxy/proxy/layers/http/__init__.py", line 488, in passthrough
for command in self.child_layer.handle_event(event):
File "/mnt/c/Users/user/git/mitmproxy/mitmproxy/proxy/layer.py", line 114, in handle_event
yield from self.__process(command_generator)
File "/mnt/c/Users/user/git/mitmproxy/mitmproxy/proxy/layer.py", line 142, in __process
command = next(command_generator)
File "/mnt/c/Users/user/git/mitmproxy/mitmproxy/proxy/tunnel.py", line 85, in _handle_event
yield from self.event_to_child(event)
File "/mnt/c/Users/user/git/mitmproxy/mitmproxy/proxy/tunnel.py", line 104, in event_to_child
for command in self.child_layer.handle_event(event):
File "/mnt/c/Users/user/git/mitmproxy/mitmproxy/proxy/layer.py", line 114, in handle_event
yield from self.__process(command_generator)
File "/mnt/c/Users/user/git/mitmproxy/mitmproxy/proxy/layer.py", line 142, in __process
command = next(command_generator)
File "/mnt/c/Users/user/git/mitmproxy/mitmproxy/proxy/tunnel.py", line 85, in _handle_event
yield from self.event_to_child(event)
File "/mnt/c/Users/user/git/mitmproxy/mitmproxy/proxy/tunnel.py", line 104, in event_to_child
for command in self.child_layer.handle_event(event):
File "/mnt/c/Users/user/git/mitmproxy/mitmproxy/proxy/layer.py", line 114, in handle_event
yield from self.__process(command_generator)
File "/mnt/c/Users/user/git/mitmproxy/mitmproxy/proxy/layer.py", line 142, in __process
command = next(command_generator)
File "/mnt/c/Users/user/git/mitmproxy/mitmproxy/proxy/layers/http/__init__.py", line 570, in _handle_event
yield from self.event_to_child(stream, event)
File "/mnt/c/Users/user/git/mitmproxy/mitmproxy/proxy/layers/http/__init__.py", line 601, in event_to_child
yield from self.event_to_child(conn, command.event)
File "/mnt/c/Users/user/git/mitmproxy/mitmproxy/proxy/layers/http/__init__.py", line 586, in event_to_child
for command in child.handle_event(event):
File "/mnt/c/Users/user/git/mitmproxy/mitmproxy/proxy/layer.py", line 114, in handle_event
yield from self.__process(command_generator)
File "/mnt/c/Users/user/git/mitmproxy/mitmproxy/proxy/layer.py", line 123, in __process
command = command_generator.send(send)
File "/mnt/c/Users/user/git/mitmproxy/mitmproxy/proxy/layers/http/_http2.py", line 266, in _handle_event
yield from super()._handle_event(event)
File "/mnt/c/Users/user/git/mitmproxy/mitmproxy/proxy/layers/http/_http2.py", line 91, in _handle_event
if stream.state_machine.state is not h2.stream.StreamState.CLOSED:
AttributeError: 'NoneType' object has no attribute 'state_machine'

# Fixed code
def _handle_event(self, event: Event) -> CommandGenerator[None]:
    if isinstance(event, Start):
        self.h2_conn.initiate_connection()
        yield SendData(self.conn, self.h2_conn.data_to_send())

    elif isinstance(event, HttpEvent):
        if isinstance(event, self.SendData):
            assert isinstance(event, (RequestData, ResponseData))
            if self.is_open_for_us(event.stream_id):
                self.h2_conn.send_data(event.stream_id, event.data)
        elif isinstance(event, self.SendEndOfMessage):
            if self.is_open_for_us(event.stream_id):
                self.h2_conn.end_stream(event.stream_id)
        elif isinstance(event, self.SendProtocolError):
            assert isinstance(event, (RequestProtocolError, ResponseProtocolError))
            if not self.is_closed(event.stream_id):
                code = {
                    status_codes.CLIENT_CLOSED_REQUEST: h2.errors.ErrorCodes.CANCEL,
                }.get(event.code, h2.errors.ErrorCodes.INTERNAL_ERROR)
                self.h2_conn.reset_stream(event.stream_id, code)
        else:
            raise AssertionError(f"Unexpected event: {event}")
        data_to_send = self.h2_conn.data_to_send()
        if data_to_send:
            yield SendData(self.conn, data_to_send)

    elif isinstance(event, DataReceived):
        try:
            try:
                events = self.h2_conn.receive_data(event.data)
            except ValueError as e:  # pragma: no cover
                # this should never raise a ValueError, but we triggered one while fuzzing:
                # https://github.com/python-hyper/hyper-h2/issues/1231
                # this stays here as defense-in-depth.
                raise h2.exceptions.ProtocolError(
                    f"uncaught hyper-h2 error: {e}"
                ) from e
        except h2.exceptions.ProtocolError as e:
            events = [e]

        for h2_event in events:
            if self.debug:
                yield Log(f"{self.debug}[h2] {h2_event}", "debug")
            if (yield from self.handle_h2_event(h2_event)):
                if self.debug:
                    yield Log(f"{self.debug}[h2] done", "debug")
                return

        data_to_send = self.h2_conn.data_to_send()
        if data_to_send:
            yield SendData(self.conn, data_to_send)

    elif isinstance(event, ConnectionClosed):
        yield from self.close_connection("peer closed connection")
    else:
        raise AssertionError(f"Unexpected event: {event!r}")