Skip to content

Livestream aborts on weak wifi: recv() uses read(n) where it needs readexactly(n) #1262

Description

@FreespiritD

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions