{"record":{"id":"cb068f27f3f18216","repo":"XX-net/XX-Net","slug":"sendbuffer-put-0","errorCode":null,"errorMessage":"SendBuffer put 0","messagePattern":"SendBuffer put 0","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"code/default/x_tunnel/local/base_container.py","lineNumber":230,"sourceCode":"        self.mutex = threading.Lock()\n        self.max_payload = max_payload\n        self.reset()\n\n    def reset(self):\n        xlog.debug(\"SendBuffer reset\")\n        self.pool_size = 0\n        self.last_put_time = time.time()\n        with self.mutex:\n            self.head_sn = 1\n            self.tail_sn = 1\n            self.block_list = {}\n            self.last_block = WriteBuffer()\n\n    def put(self, data):\n        dlen = len(data)\n        # xlog.debug(\"SendBuffer len:%d\", dlen)\n        if dlen == 0:\n            xlog.warn(\"SendBuffer put 0\")\n            return False\n\n        # xlog.debug(\"SendBuffer put len:%d\", len(data))\n        self.last_put_time = time.time()\n        with self.mutex:\n            self.pool_size += dlen\n            self.last_block.append(data)\n\n            if len(self.last_block) > self.max_payload:\n                self.block_list[self.head_sn] = self.last_block\n                self.last_block = WriteBuffer()\n                self.head_sn += 1\n        return True\n\n    def get(self):\n        with self.mutex:\n            if self.tail_sn < self.head_sn:\n                data = self.block_list[self.tail_sn]","sourceCodeStart":212,"sourceCodeEnd":248,"githubUrl":"https://github.com/XX-net/XX-Net/blob/cfa5bc17b67676e467f37ec50766127e0ab5f0aa/code/default/x_tunnel/local/base_container.py#L212-L248","documentation":"SendBuffer.put() was called with an empty bytes object. It refuses to add zero-length data (returns False) because an empty buffer block would corrupt the send-stream accounting.","triggerScenarios":"Caller passes b'' to SendBuffer.put, typically when an upstream read returned 0 bytes but was still forwarded, or a caller didn't check for empty payloads.","commonSituations":"EOF handling that forwards the empty read result; encoder returning empty output for empty input; edge-case in chunked assembly producing empty frames.","solutions":["Find the caller passing empty data and skip sending when the read/payload is empty","Guard upstream: if not data: return before calling put","Add an assert/log at the caller to identify which path produces empty buffers","Treat empty payload as EOF signal if that is the semantic"],"exampleFix":"# before\nbuf.put(data)\n# after\nif data:\n    buf.put(data)","handlingStrategy":"validation","validationCode":"if not data: return False  # before calling put","typeGuard":"def has_payload(d): return len(d) > 0","tryCatchPattern":null,"preventionTips":["Never forward empty reads; treat them as EOF","Check payload length at the producer side"],"tags":["buffer","empty-payload","validation"],"backgroundTag":"empty-payload-passed","analyzedSha":"cfa5bc17b67676e467f37ec50766127e0ab5f0aa","analyzedAt":"2026-08-27T19:28:28.225Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}