{"record":{"id":"f24eafd063c68263","repo":"AlexxIT/go2rtc","slug":"pop-buffer-is-full","errorCode":null,"errorMessage":"pop buffer is full","messagePattern":"pop buffer is full","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/xiaomi/miss/cs2/conn.go","lineNumber":448,"sourceCode":"}\n\nfunc (c *dataChannel) Push(b []byte) error {\n\tc.waitData = append(c.waitData, b...)\n\n\tfor len(c.waitData) > 4 {\n\t\t// Every new data starts with size. There can be several data inside one packet.\n\t\tif c.waitSize == 0 {\n\t\t\tc.waitSize = int(binary.BigEndian.Uint32(c.waitData))\n\t\t\tc.waitData = c.waitData[4:]\n\t\t}\n\t\tif c.waitSize > len(c.waitData) {\n\t\t\tbreak\n\t\t}\n\n\t\tselect {\n\t\tcase c.popBuf <- c.waitData[:c.waitSize]:\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"pop buffer is full\")\n\t\t}\n\n\t\tc.waitData = c.waitData[c.waitSize:]\n\t\tc.waitSize = 0\n\t}\n\treturn nil\n}\n\nfunc (c *dataChannel) Pop() ([]byte, bool) {\n\tdata, ok := <-c.popBuf\n\treturn data, ok\n}\n\nfunc (c *dataChannel) Close() {\n\tclose(c.popBuf)\n}\n\n// PushSeq returns how many seq were processed.","sourceCodeStart":430,"sourceCodeEnd":466,"githubUrl":"https://github.com/AlexxIT/go2rtc/blob/c245815e75e2a5fd60b4290f12bfc04e55a984d3/pkg/xiaomi/miss/cs2/conn.go#L430-L466","documentation":"Push delivers a completed data frame by sending it on the c.popBuf channel without blocking; when the channel's buffer is full (the consumer isn't draining fast enough) it returns \"pop buffer is full\" immediately. It is a backpressure signal: frames would otherwise block the worker or be lost, so the library fails fast instead.","triggerScenarios":"Calling Push (via PushSeq) when the application is not draining popBuf fast enough — typically during bursts of incoming frames while the consumer is blocked, slow, or has exited.","commonSituations":"Consumer goroutine exited or is stuck in slow processing; burst of frames after a reconnect; popBuf capacity too small for the data rate; forgetting to consume in a fire-and-forget setup.","solutions":["Ensure a dedicated consumer goroutine continuously drains the connection's receive channel","Increase the popBuf channel capacity at connection creation to absorb bursts","On this error, reconnect or resync sequence numbers — the frame was NOT delivered, so handle retransmission (PushSeq sequencing helps detect gaps)","Apply backpressure upstream (pause the sender or reduce inflight commands) instead of letting frames queue unboundedly"],"exampleFix":"// before\nfor data := range frames {\n    slowProcess(data) // blocks; popBuf fills up\n}\n// after\ngo func() {\n    for data := range frames {\n        go slowProcess(data) // keep draining popBuf promptly\n    }\n}()","handlingStrategy":"fallback","validationCode":"// drain pending frames before pushing more, or check channel occupancy\nif len(popBuf) == cap(popBuf) {\n    return fmt.Errorf(\"consumer stalled: pop buffer full\")\n}","typeGuard":"func consumerHealthy(popBuf chan []byte) bool { return len(popBuf) < cap(popBuf) }","tryCatchPattern":"if err := conn.Push(frame); err != nil {\n    if strings.Contains(err.Error(), \"pop buffer is full\") {\n        // backpressure: drop/requeue frame and resync via seq numbers\n        metrics.BackpressureInc()\n        return requeue(frame)\n    }\n    return err\n}","preventionTips":["Run a dedicated goroutine that continuously drains the receive channel","Size popBuf capacity for the expected burst rate","Monitor channel occupancy and apply upstream backpressure early","On frame drop, use PushSeq sequence numbers to detect and recover gaps"],"tags":["backpressure","buffer","channel","xiaomi-cs2"],"backgroundTag":"channel-buffer-full","analyzedSha":"c245815e75e2a5fd60b4290f12bfc04e55a984d3","analyzedAt":"2026-09-07T11:47:02.965Z","contentChangedAt":"2026-09-07T11:47:02.965Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}