XX-net/XX-Net · warning

Except %s flush_send_s BlockingIOError %r

Error message

Except %s flush_send_s BlockingIOError %r

What it means

Logged when flush_send_s() raises BlockingIOError while writing split-SNI buffered data to the non-blocking socket s2 during the pipe loop. The send buffer is full so the OS would block; the write side of s2 is closed and the loop continues.

Source

Thrown at code/default/smart_router/local/pipe_socks.py:262

                                    s2.port == 443 and \
                                    d[0] == '\x16' and \
                            g.gfwlist.in_block_list(s2.host):
                        p1 = d.find(s2.host)
                        if p1 > 1:
                            if b"google" in s2.host:
                                p2 = d.find(b"google") + 3
                            else:
                                p2 = p1 + len(s2.host) - 6

                            d1 = d[:p2]
                            d2 = d[p2:]

                            try:
                                flush_send_s(s2, d1)
                                s2.sent_data += len(d1)
                                s2.sent_times += 1
                            except BlockingIOError as e:
                                xlog.warn("Except %s flush_send_s BlockingIOError %r", s2, e)
                                self.close(s2, "w")
                                continue
                            except Exception as e:
                                xlog.warn("send split SNI:%s fail:%r", s2.host, e)
                                self.close(s2, "w")
                                continue

                            s2.add_dat(d2)
                            d = b""
                            xlog.debug("pipe send split SNI:%s", s2.host)

                    if s2.buf_size == 0:
                        try:
                            sent = s2.send(d)
                            s2.sent_data += sent
                            s2.sent_times += 1
                            # xlog.debug("direct send %d to %s from:%s total:%d", sent, s2, s1, len(d))
                        except BlockingIOError as e:

View on GitHub (pinned to cfa5bc17b6)

Solutions

  1. Tune/raise SO_SNDBUF on s2 or reduce the amount of buffered data before flushing
  2. Enable/verify the buffering path so partial sends queue instead of closing the write side
  3. Check remote peer health and network throughput; close dead connections earlier
  4. Retry flush later instead of closing the write side on transient EAGAIN

Example fix

// before
except BlockingIOError as e:
    xlog.warn("Except %s flush_send_s BlockingIOError %r", s2, e)
    self.close(s2, "w")
// after
except BlockingIOError as e:
    xlog.warn("Except %s flush_send_s BlockingIOError %r", s2, e)
    s2.set_blocking_wakeup()  # defer flush to next epoll/select cycle
Defensive patterns

Strategy: retry

Validate before calling

if s2.buffered_size > HIGH_WATER: defer_flush()  # wait for writability

Try / catch

Catch BlockingIOError separately from socket.error and treat as 'retry later', not fatal.

Prevention

When it happens

Trigger: Non-blocking socket s2 whose send buffer is full (slow remote peer / fast local writer) when pipe() tries to flush buffered data after SNI splitting.

Common situations: Slow upstream server or throttled link while large buffered data is flushed; mismatched socket timeouts; peer stalled.

Related errors


AI-assisted analysis of XX-net/XX-Net@cfa5bc17b6 (2026-08-27). Data as JSON: /api/errors/2a434f29362d62e4. Report an issue: GitHub.