TCP message blocking issue #28

Closed
opened 2026-02-15 21:15:22 -05:00 by yindo · 6 comments
Owner

Originally created by @Exhen on GitHub (Apr 15, 2025).

Encountered a TCP message blocking issue while debugging the plugin. Interestingly, this issue only occurs during local plugin debugging. For example, when debugging the following code, message 1 can be returned normally, but message 2 gets blocked.

Code example:

class TestTool(Tool):
    def _invoke(self, tool_parameters: dict[str, Any]) -> Generator[ToolInvokeMessage]:
        print("Starting to send message 1")
        yield self.create_text_message("Connection test 1")
        
        print("Preparing to sleep")
        time.sleep(1)
        print("Sleep completed")

        print("Starting to send message 2")
        yield self.create_text_message("Connection test 2")

here's a working fix for me (generated by Cursor)

            if native_socket.socket is gevent_socket.socket:
                """
                gevent socket is non-blocking, to avoid BlockingIOError
                send data bytes by bytes
                """
                data_bytes = data.encode()
                # while data_bytes:
                current_time = time.time()
                if current_time - self.last_send_time < 0.1:  
                    sleep(0.1) 
                
                try:
                    sent = self.sock.send(data_bytes)
                    self.last_send_time = time.time()
                    
                
                    
                except BlockingIOError as e:
                    if e.errno != errno.EAGAIN:
                        raise
                    sleep(0.1)
                    raise
Originally created by @Exhen on GitHub (Apr 15, 2025). Encountered a TCP message blocking issue while debugging the plugin. Interestingly, this issue only occurs during local plugin debugging. For example, when debugging the following code, message 1 can be returned normally, but message 2 gets blocked. Code example: ```python class TestTool(Tool): def _invoke(self, tool_parameters: dict[str, Any]) -> Generator[ToolInvokeMessage]: print("Starting to send message 1") yield self.create_text_message("Connection test 1") print("Preparing to sleep") time.sleep(1) print("Sleep completed") print("Starting to send message 2") yield self.create_text_message("Connection test 2") ``` here's a working fix for me (generated by Cursor) ```python if native_socket.socket is gevent_socket.socket: """ gevent socket is non-blocking, to avoid BlockingIOError send data bytes by bytes """ data_bytes = data.encode() # while data_bytes: current_time = time.time() if current_time - self.last_send_time < 0.1: sleep(0.1) try: sent = self.sock.send(data_bytes) self.last_send_time = time.time() except BlockingIOError as e: if e.errno != errno.EAGAIN: raise sleep(0.1) raise ```
yindo closed this issue 2026-02-15 21:15:22 -05:00
Author
Owner

@dooonabe commented on GitHub (Apr 21, 2025):

Encountered a TCP message blocking issue while debugging the plugin. Interestingly, this issue only occurs during local plugin debugging. For example, when debugging the following code, message 1 can be returned normally, but message 2 gets blocked.

Code example:

class TestTool(Tool):
def _invoke(self, tool_parameters: dict[str, Any]) -> Generator[ToolInvokeMessage]:
print("Starting to send message 1")
yield self.create_text_message("Connection test 1")

    print("Preparing to sleep")
    time.sleep(1)
    print("Sleep completed")

    print("Starting to send message 2")
    yield self.create_text_message("Connection test 2")

here's a working fix for me (generated by Cursor)

        if native_socket.socket is gevent_socket.socket:
            """
            gevent socket is non-blocking, to avoid BlockingIOError
            send data bytes by bytes
            """
            data_bytes = data.encode()
            # while data_bytes:
            current_time = time.time()
            if current_time - self.last_send_time < 0.1:  
                sleep(0.1) 
            
            try:
                sent = self.sock.send(data_bytes)
                self.last_send_time = time.time()
                
            
                
            except BlockingIOError as e:
                if e.errno != errno.EAGAIN:
                    raise
                sleep(0.1)
                raise

thanks, it's work for me too!!! ps:add self.last_send_time = 0 in TCPReaderWriter init function

@dooonabe commented on GitHub (Apr 21, 2025): > Encountered a TCP message blocking issue while debugging the plugin. Interestingly, this issue only occurs during local plugin debugging. For example, when debugging the following code, message 1 can be returned normally, but message 2 gets blocked. > > Code example: > > class TestTool(Tool): > def _invoke(self, tool_parameters: dict[str, Any]) -> Generator[ToolInvokeMessage]: > print("Starting to send message 1") > yield self.create_text_message("Connection test 1") > > print("Preparing to sleep") > time.sleep(1) > print("Sleep completed") > > print("Starting to send message 2") > yield self.create_text_message("Connection test 2") > here's a working fix for me (generated by Cursor) > > if native_socket.socket is gevent_socket.socket: > """ > gevent socket is non-blocking, to avoid BlockingIOError > send data bytes by bytes > """ > data_bytes = data.encode() > # while data_bytes: > current_time = time.time() > if current_time - self.last_send_time < 0.1: > sleep(0.1) > > try: > sent = self.sock.send(data_bytes) > self.last_send_time = time.time() > > > > except BlockingIOError as e: > if e.errno != errno.EAGAIN: > raise > sleep(0.1) > raise thanks, it's work for me too!!! ps:add self.last_send_time = 0 in TCPReaderWriter __init__ function
Author
Owner

@dooonabe commented on GitHub (Apr 21, 2025):

and i find the blocking reason: _recv_from_sock contains lock because this error from debug.dify.ai:5003
Image

Image

but why?

@dooonabe commented on GitHub (Apr 21, 2025): and i find the blocking reason: _recv_from_sock contains lock because this error from debug.dify.ai:5003 ![Image](https://github.com/user-attachments/assets/7e1964bb-cbfb-4eb7-8018-31e8ae1f98ed) ![Image](https://github.com/user-attachments/assets/a0fb28a9-cb9c-4569-94ac-ab449b0b28a1) but why?
Author
Owner

@Exhen commented on GitHub (Apr 21, 2025):

and i find the blocking reason: _recv_from_sock contains lock because this error from debug.dify.ai:5003 Image

Image

but why?

something with sync/async methods I guess?

@Exhen commented on GitHub (Apr 21, 2025): > and i find the blocking reason: _recv_from_sock contains lock because this error from debug.dify.ai:5003 ![Image](https://github.com/user-attachments/assets/7e1964bb-cbfb-4eb7-8018-31e8ae1f98ed) > > ![Image](https://github.com/user-attachments/assets/a0fb28a9-cb9c-4569-94ac-ab449b0b28a1) > > but why? something with sync/async methods I guess?
Author
Owner

@LeeeeeeM commented on GitHub (Apr 25, 2025):

I met this problem also.

https://github.com/langgenius/dify/issues/18729

@LeeeeeeM commented on GitHub (Apr 25, 2025): I met this problem also. https://github.com/langgenius/dify/issues/18729
Author
Owner

@zengruizhao commented on GitHub (Apr 29, 2025):

Yes, I think the lock should not be added here. After adding the lock, the lock is always acquired by receive, and send cannot acquire the lock.

@zengruizhao commented on GitHub (Apr 29, 2025): Yes, I think the lock should not be added here. After adding the lock, the lock is always acquired by receive, and send cannot acquire the lock.
Author
Owner

@zengruizhao commented on GitHub (Apr 30, 2025):

This problem was found on Windows, but not on Linux. The reason for the problem is whether the socket is blocked. After using gevent, it was changed to non-blocking mode, but it does not work on Windows.

ready_to_read, _, _ = select([self.sock], [], [], 1)
if not ready_to_read:
    continue

In Windows, as long as the socket is readable once, it will be readable afterwards, so it will inevitably block at the subsequent socket.recv,while Linux will make a judgment every time.

@zengruizhao commented on GitHub (Apr 30, 2025): This problem was found on Windows, but not on Linux. The reason for the problem is whether the socket is blocked. After using gevent, it was changed to non-blocking mode, but it does not work on Windows. ```python ready_to_read, _, _ = select([self.sock], [], [], 1) if not ready_to_read: continue ``` In Windows, as long as the socket is readable once, it will be readable afterwards, so it will inevitably block at the subsequent socket.recv,while Linux will make a judgment every time.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langgenius/dify-plugin-sdks#28