Summary
BlinkLiveStream.recv() reads the IMMI stream with StreamReader.read(n), which returns up to n bytes, and then treats a short read as a fatal error. Any payload split across TCP segments kills the stream within seconds. readexactly(n) is the call that was intended.
This only bites when packets actually fragment, which is why it works fine for most people. On a camera with a marginal signal it fails every time.
Symptom
mycamera: streaming.
Insufficient data for payload: 114 bytes, expected 1316
The stream then tears down. Reproduces reliably on a camera at roughly -68 dBm; the same code path is fine on a camera with a strong signal.
Cause
In blinkpy/livestream.py, recv() (still present on dev as of 13 July 2026):
data = await self.target_reader.read(9)
if len(data) < 9:
_LOGGER.warning("Insufficient data for header: %d bytes, expected 9", len(data))
break
...
data = await self.target_reader.read(payload_length)
if len(data) < payload_length:
_LOGGER.warning(
"Insufficient data for payload: %d bytes, expected %d", len(data), payload_length
)
break
asyncio.StreamReader.read(n) is documented to read up to n bytes: it returns whatever is in the buffer as soon as anything is available, and a short return is normal, not an error. The two len(data) < n guards are therefore treating ordinary TCP segmentation as stream corruption.
There is a second, quieter consequence of the same bug. When the short read happens mid-payload, the loop breaks with the remaining bytes of that payload still unread, so the stream is left desynchronised. Even without the break, the subsequent header read would land in the middle of a payload.
Fix
readexactly(n) blocks until the full n bytes have arrived and raises IncompleteReadError if the peer closes first, which is the only case that genuinely means "stream over":
try:
header = await self.target_reader.readexactly(9)
except asyncio.IncompleteReadError:
break
msgtype = header[0]
payload_length = int.from_bytes(header[5:9], byteorder="big")
if payload_length <= 0:
continue
try:
data = await self.target_reader.readexactly(payload_length)
except asyncio.IncompleteReadError:
break
I have been running this patched (as a monkeypatch, not a fork) and live view is stable where it previously died within seconds. Happy to open a PR if that is useful.
Environment
- blinkpy 0.25.7 (bug also present on
dev)
- Python 3.11.2, Raspberry Pi OS (aarch64), Raspberry Pi 500
- One Blink camera,
immis:// stream, wifi ~-68 dBm
Summary
BlinkLiveStream.recv()reads the IMMI stream withStreamReader.read(n), which returns up to n bytes, and then treats a short read as a fatal error. Any payload split across TCP segments kills the stream within seconds.readexactly(n)is the call that was intended.This only bites when packets actually fragment, which is why it works fine for most people. On a camera with a marginal signal it fails every time.
Symptom
The stream then tears down. Reproduces reliably on a camera at roughly -68 dBm; the same code path is fine on a camera with a strong signal.
Cause
In
blinkpy/livestream.py,recv()(still present ondevas of 13 July 2026):asyncio.StreamReader.read(n)is documented to read up to n bytes: it returns whatever is in the buffer as soon as anything is available, and a short return is normal, not an error. The twolen(data) < nguards are therefore treating ordinary TCP segmentation as stream corruption.There is a second, quieter consequence of the same bug. When the short read happens mid-payload, the loop breaks with the remaining bytes of that payload still unread, so the stream is left desynchronised. Even without the
break, the subsequent header read would land in the middle of a payload.Fix
readexactly(n)blocks until the full n bytes have arrived and raisesIncompleteReadErrorif the peer closes first, which is the only case that genuinely means "stream over":I have been running this patched (as a monkeypatch, not a fork) and live view is stable where it previously died within seconds. Happy to open a PR if that is useful.
Environment
dev)immis://stream, wifi ~-68 dBm